13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 */
24
25 // This file is available under and governed by the GNU General Public
26 // License version 2 only, as published by the Free Software Foundation.
27 // However, the following notice accompanied the original version of this
28 // file:
29 //
30 //---------------------------------------------------------------------------------
31 //
32 // Little Color Management System
33 // Copyright (c) 1998-2012 Marti Maria Saguer
34 //
35 // Permission is hereby granted, free of charge, to any person obtaining
36 // a copy of this software and associated documentation files (the "Software"),
37 // to deal in the Software without restriction, including without limitation
38 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
39 // and/or sell copies of the Software, and to permit persons to whom the Software
40 // is furnished to do so, subject to the following conditions:
41 //
42 // The above copyright notice and this permission notice shall be included in
43 // all copies or substantial portions of the Software.
44 //
45 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
46 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
47 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
48 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
49 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
50 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
51 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
52 //
53 //---------------------------------------------------------------------------------
336 if (iohandler) _cmsFree(ContextID, iohandler);
337 return NULL;
338 }
339
340 // File-based stream -------------------------------------------------------
341
342 // Read count elements of size bytes each. Return number of elements read
343 static
344 cmsUInt32Number FileRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count)
345 {
346 cmsUInt32Number nReaded = (cmsUInt32Number) fread(Buffer, size, count, (FILE*) iohandler->stream);
347
348 if (nReaded != count) {
349 cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Read error. Got %d bytes, block should be of %d bytes", nReaded * size, count * size);
350 return 0;
351 }
352
353 return nReaded;
354 }
355
356 // Postion file pointer in the file
357 static
358 cmsBool FileSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset)
359 {
360 if (fseek((FILE*) iohandler ->stream, (long) offset, SEEK_SET) != 0) {
361
362 cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Seek error; probably corrupted file");
363 return FALSE;
364 }
365
366 return TRUE;
367 }
368
369 // Returns file pointer position
370 static
371 cmsUInt32Number FileTell(cmsIOHANDLER* iohandler)
372 {
373 return (cmsUInt32Number) ftell((FILE*)iohandler ->stream);
374 }
375
376 // Writes data to stream, also keeps used space for further reference. Returns TRUE on success, FALSE on error
380 if (size == 0) return TRUE; // We allow to write 0 bytes, but nothing is written
381
382 iohandler->UsedSpace += size;
383 return (fwrite(Buffer, size, 1, (FILE*) iohandler->stream) == 1);
384 }
385
386 // Closes the file
387 static
388 cmsBool FileClose(cmsIOHANDLER* iohandler)
389 {
390 if (fclose((FILE*) iohandler ->stream) != 0) return FALSE;
391 _cmsFree(iohandler ->ContextID, iohandler);
392 return TRUE;
393 }
394
395 // Create a iohandler for disk based files.
396 cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const char* FileName, const char* AccessMode)
397 {
398 cmsIOHANDLER* iohandler = NULL;
399 FILE* fm = NULL;
400
401 _cmsAssert(FileName != NULL);
402 _cmsAssert(AccessMode != NULL);
403
404 iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER));
405 if (iohandler == NULL) return NULL;
406
407 switch (*AccessMode) {
408
409 case 'r':
410 fm = fopen(FileName, "rb");
411 if (fm == NULL) {
412 _cmsFree(ContextID, iohandler);
413 cmsSignalError(ContextID, cmsERROR_FILE, "File '%s' not found", FileName);
414 return NULL;
415 }
416 iohandler -> ReportedSize = (cmsUInt32Number) cmsfilelength(fm);
417 break;
418
419 case 'w':
420 fm = fopen(FileName, "wb");
421 if (fm == NULL) {
422 _cmsFree(ContextID, iohandler);
423 cmsSignalError(ContextID, cmsERROR_FILE, "Couldn't create '%s'", FileName);
424 return NULL;
425 }
426 iohandler -> ReportedSize = 0;
427 break;
428
429 default:
430 _cmsFree(ContextID, iohandler);
431 cmsSignalError(ContextID, cmsERROR_FILE, "Unknown access mode '%c'", *AccessMode);
432 return NULL;
433 }
434
435 iohandler ->ContextID = ContextID;
436 iohandler ->stream = (void*) fm;
437 iohandler ->UsedSpace = 0;
438
439 // Keep track of the original file
440 strncpy(iohandler -> PhysicalFile, FileName, sizeof(iohandler -> PhysicalFile)-1);
441 iohandler -> PhysicalFile[sizeof(iohandler -> PhysicalFile)-1] = 0;
442
443 iohandler ->Read = FileRead;
444 iohandler ->Seek = FileSeek;
445 iohandler ->Close = FileClose;
446 iohandler ->Tell = FileTell;
447 iohandler ->Write = FileWrite;
448
449 return iohandler;
450 }
451
452 // Create a iohandler for stream based files
453 cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromStream(cmsContext ContextID, FILE* Stream)
454 {
455 cmsIOHANDLER* iohandler = NULL;
456
457 iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER));
458 if (iohandler == NULL) return NULL;
459
460 iohandler -> ContextID = ContextID;
461 iohandler -> stream = (void*) Stream;
462 iohandler -> UsedSpace = 0;
463 iohandler -> ReportedSize = (cmsUInt32Number) cmsfilelength(Stream);
464 iohandler -> PhysicalFile[0] = 0;
465
466 iohandler ->Read = FileRead;
467 iohandler ->Seek = FileSeek;
468 iohandler ->Close = FileClose;
469 iohandler ->Tell = FileTell;
470 iohandler ->Write = FileWrite;
471
472 return iohandler;
473 }
474
475
476
477 // Close an open IO handler
478 cmsBool CMSEXPORT cmsCloseIOhandler(cmsIOHANDLER* io)
479 {
480 return io -> Close(io);
481 }
482
483 // -------------------------------------------------------------------------------------------------------
635 _cmsDeleteTagByPos(Icc, i);
636 *NewPos = i;
637 }
638 else {
639
640 // No, make a new one
641
642 if (Icc -> TagCount >= MAX_TABLE_TAG) {
643 cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", MAX_TABLE_TAG);
644 return FALSE;
645 }
646
647 *NewPos = Icc ->TagCount;
648 Icc -> TagCount++;
649 }
650
651 return TRUE;
652 }
653
654
655 // Check existance
656 cmsBool CMSEXPORT cmsIsTag(cmsHPROFILE hProfile, cmsTagSignature sig)
657 {
658 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) (void*) hProfile;
659 return _cmsSearchTag(Icc, sig, FALSE) >= 0;
660 }
661
662
663
664 // Enforces that the profile version is per. spec.
665 // Operates on the big endian bytes from the profile.
666 // Called before converting to platform endianness.
667 // Byte 0 is BCD major version, so max 9.
668 // Byte 1 is 2 BCD digits, one per nibble.
669 // Reserved bytes 2 & 3 must be 0.
670 static
671 cmsUInt32Number _validatedVersion(cmsUInt32Number DWord)
672 {
673 cmsUInt8Number* pByte = (cmsUInt8Number*) &DWord;
674 cmsUInt8Number temp1;
675 cmsUInt8Number temp2;
691 {
692 cmsTagEntry Tag;
693 cmsICCHeader Header;
694 cmsUInt32Number i, j;
695 cmsUInt32Number HeaderSize;
696 cmsIOHANDLER* io = Icc ->IOhandler;
697 cmsUInt32Number TagCount;
698
699
700 // Read the header
701 if (io -> Read(io, &Header, sizeof(cmsICCHeader), 1) != 1) {
702 return FALSE;
703 }
704
705 // Validate file as an ICC profile
706 if (_cmsAdjustEndianess32(Header.magic) != cmsMagicNumber) {
707 cmsSignalError(Icc ->ContextID, cmsERROR_BAD_SIGNATURE, "not an ICC profile, invalid signature");
708 return FALSE;
709 }
710
711 // Adjust endianess of the used parameters
712 Icc -> DeviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Header.deviceClass);
713 Icc -> ColorSpace = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.colorSpace);
714 Icc -> PCS = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.pcs);
715
716 Icc -> RenderingIntent = _cmsAdjustEndianess32(Header.renderingIntent);
717 Icc -> flags = _cmsAdjustEndianess32(Header.flags);
718 Icc -> manufacturer = _cmsAdjustEndianess32(Header.manufacturer);
719 Icc -> model = _cmsAdjustEndianess32(Header.model);
720 Icc -> creator = _cmsAdjustEndianess32(Header.creator);
721
722 _cmsAdjustEndianess64(&Icc -> attributes, &Header.attributes);
723 Icc -> Version = _cmsAdjustEndianess32(_validatedVersion(Header.version));
724
725 // Get size as reported in header
726 HeaderSize = _cmsAdjustEndianess32(Header.size);
727
728 // Make sure HeaderSize is lower than profile size
729 if (HeaderSize >= Icc ->IOhandler ->ReportedSize)
730 HeaderSize = Icc ->IOhandler ->ReportedSize;
731
809
810 Header.flags = _cmsAdjustEndianess32(Icc -> flags);
811 Header.manufacturer = _cmsAdjustEndianess32(Icc -> manufacturer);
812 Header.model = _cmsAdjustEndianess32(Icc -> model);
813
814 _cmsAdjustEndianess64(&Header.attributes, &Icc -> attributes);
815
816 // Rendering intent in the header (for embedded profiles)
817 Header.renderingIntent = _cmsAdjustEndianess32(Icc -> RenderingIntent);
818
819 // Illuminant is always D50
820 Header.illuminant.X = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->X));
821 Header.illuminant.Y = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->Y));
822 Header.illuminant.Z = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->Z));
823
824 // Created by LittleCMS (that's me!)
825 Header.creator = _cmsAdjustEndianess32(lcmsSignature);
826
827 memset(&Header.reserved, 0, sizeof(Header.reserved));
828
829 // Set profile ID. Endianess is always big endian
830 memmove(&Header.profileID, &Icc ->ProfileID, 16);
831
832 // Dump the header
833 if (!Icc -> IOhandler->Write(Icc->IOhandler, sizeof(cmsICCHeader), &Header)) return FALSE;
834
835 // Saves Tag directory
836
837 // Get true count
838 for (i=0; i < Icc -> TagCount; i++) {
839 if (Icc ->TagNames[i] != 0)
840 Count++;
841 }
842
843 // Store number of tags
844 if (!_cmsWriteUInt32Number(Icc ->IOhandler, Count)) return FALSE;
845
846 for (i=0; i < Icc -> TagCount; i++) {
847
848 if (Icc ->TagNames[i] == 0) continue; // It is just a placeholder
849
850 Tag.sig = (cmsTagSignature) _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagNames[i]);
851 Tag.offset = _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagOffsets[i]);
852 Tag.size = _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagSizes[i]);
853
854 if (!Icc ->IOhandler -> Write(Icc-> IOhandler, sizeof(cmsTagEntry), &Tag)) return FALSE;
855 }
856
857 return TRUE;
858 }
859
860 // ----------------------------------------------------------------------- Set/Get several struct members
861
862
863 cmsUInt32Number CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile)
864 {
865 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
866 return Icc -> RenderingIntent;
867 }
868
1178
1179
1180
1181 // Dump tag contents. If the profile is being modified, untouched tags are copied from FileOrig
1182 static
1183 cmsBool SaveTags(_cmsICCPROFILE* Icc, _cmsICCPROFILE* FileOrig)
1184 {
1185 cmsUInt8Number* Data;
1186 cmsUInt32Number i;
1187 cmsUInt32Number Begin;
1188 cmsIOHANDLER* io = Icc ->IOhandler;
1189 cmsTagDescriptor* TagDescriptor;
1190 cmsTagTypeSignature TypeBase;
1191 cmsTagTypeSignature Type;
1192 cmsTagTypeHandler* TypeHandler;
1193 cmsFloat64Number Version = cmsGetProfileVersion((cmsHPROFILE) Icc);
1194 cmsTagTypeHandler LocalTypeHandler;
1195
1196 for (i=0; i < Icc -> TagCount; i++) {
1197
1198 if (Icc ->TagNames[i] == 0) continue;
1199
1200 // Linked tags are not written
1201 if (Icc ->TagLinked[i] != (cmsTagSignature) 0) continue;
1202
1203 Icc -> TagOffsets[i] = Begin = io ->UsedSpace;
1204
1205 Data = (cmsUInt8Number*) Icc -> TagPtrs[i];
1206
1207 if (!Data) {
1208
1209 // Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user.
1210 // In this case a blind copy of the block data is performed
1211 if (FileOrig != NULL && Icc -> TagOffsets[i]) {
1212
1213 cmsUInt32Number TagSize = FileOrig -> TagSizes[i];
1214 cmsUInt32Number TagOffset = FileOrig -> TagOffsets[i];
1215 void* Mem;
1216
1217 if (!FileOrig ->IOhandler->Seek(FileOrig ->IOhandler, TagOffset)) return FALSE;
1218
1312
1313 }
1314 }
1315
1316 return TRUE;
1317 }
1318
1319 // Low-level save to IOHANDLER. It returns the number of bytes used to
1320 // store the profile, or zero on error. io may be NULL and in this case
1321 // no data is written--only sizes are calculated
1322 cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOHANDLER* io)
1323 {
1324 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
1325 _cmsICCPROFILE Keep;
1326 cmsIOHANDLER* PrevIO = NULL;
1327 cmsUInt32Number UsedSpace;
1328 cmsContext ContextID;
1329
1330 _cmsAssert(hProfile != NULL);
1331
1332 memmove(&Keep, Icc, sizeof(_cmsICCPROFILE));
1333
1334 ContextID = cmsGetProfileContextID(hProfile);
1335 PrevIO = Icc ->IOhandler = cmsOpenIOhandlerFromNULL(ContextID);
1336 if (PrevIO == NULL) return 0;
1337
1338 // Pass #1 does compute offsets
1339
1340 if (!_cmsWriteHeader(Icc, 0)) goto Error;
1341 if (!SaveTags(Icc, &Keep)) goto Error;
1342
1343 UsedSpace = PrevIO ->UsedSpace;
1344
1345 // Pass #2 does save to iohandler
1346
1347 if (io != NULL) {
1348
1349 Icc ->IOhandler = io;
1350 if (!SetLinks(Icc)) goto Error;
1351 if (!_cmsWriteHeader(Icc, UsedSpace)) goto Error;
1352 if (!SaveTags(Icc, &Keep)) goto Error;
1353 }
1354
1355 memmove(Icc, &Keep, sizeof(_cmsICCPROFILE));
1356 if (!cmsCloseIOhandler(PrevIO)) return 0;
1357
1358 return UsedSpace;
1359
1360
1361 Error:
1362 cmsCloseIOhandler(PrevIO);
1363 memmove(Icc, &Keep, sizeof(_cmsICCPROFILE));
1364 return 0;
1365 }
1366
1367
1368 // Low-level save to disk.
1369 cmsBool CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName)
1370 {
1371 cmsContext ContextID = cmsGetProfileContextID(hProfile);
1372 cmsIOHANDLER* io = cmsOpenIOhandlerFromFile(ContextID, FileName, "w");
1373 cmsBool rc;
1374
1375 if (io == NULL) return FALSE;
1376
1377 rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0);
1378 rc &= cmsCloseIOhandler(io);
1379
1380 if (rc == FALSE) { // remove() is C99 per 7.19.4.1
1381 remove(FileName); // We have to IGNORE return value in this case
1382 }
1383 return rc;
1547 BaseType = _cmsReadTypeBase(io);
1548 if (BaseType == 0) goto Error;
1549
1550 if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error;
1551
1552 TagSize -= 8; // Alredy read by the type base logic
1553
1554 // Get type handler
1555 TypeHandler = _cmsGetTagTypeHandler(Icc ->ContextID, BaseType);
1556 if (TypeHandler == NULL) goto Error;
1557 LocalTypeHandler = *TypeHandler;
1558
1559
1560 // Read the tag
1561 Icc -> TagTypeHandlers[n] = TypeHandler;
1562
1563 LocalTypeHandler.ContextID = Icc ->ContextID;
1564 LocalTypeHandler.ICCVersion = Icc ->Version;
1565 Icc -> TagPtrs[n] = LocalTypeHandler.ReadPtr(&LocalTypeHandler, io, &ElemCount, TagSize);
1566
1567 // The tag type is supported, but something wrong happend and we cannot read the tag.
1568 // let know the user about this (although it is just a warning)
1569 if (Icc -> TagPtrs[n] == NULL) {
1570
1571 char String[5];
1572
1573 _cmsTagSignature2String(String, sig);
1574 cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted tag '%s'", String);
1575 goto Error;
1576 }
1577
1578 // This is a weird error that may be a symptom of something more serious, the number of
1579 // stored item is actually less than the number of required elements.
1580 if (ElemCount < TagDescriptor ->ElemCount) {
1581
1582 char String[5];
1583
1584 _cmsTagSignature2String(String, sig);
1585 cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "'%s' Inconsistent number of items: expected %d, got %d",
1586 String, TagDescriptor ->ElemCount, ElemCount);
1587 }
1866
1867 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0;
1868
1869 if (!_cmsNewTag(Icc, sig, &i)) {
1870 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1871 return FALSE;
1872 }
1873
1874 // Mark the tag as being written as RAW
1875 Icc ->TagSaveAsRaw[i] = TRUE;
1876 Icc ->TagNames[i] = sig;
1877 Icc ->TagLinked[i] = (cmsTagSignature) 0;
1878
1879 // Keep a copy of the block
1880 Icc ->TagPtrs[i] = _cmsDupMem(Icc ->ContextID, data, Size);
1881 Icc ->TagSizes[i] = Size;
1882
1883 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1884
1885 if (Icc->TagPtrs[i] == NULL) {
1886 Icc->TagNames[i] = 0;
1887 return FALSE;
1888 }
1889 return TRUE;
1890 }
1891
1892 // Using this function you can collapse several tag entries to the same block in the profile
1893 cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSignature dest)
1894 {
1895 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
1896 int i;
1897
1898 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE;
1899
1900 if (!_cmsNewTag(Icc, sig, &i)) {
1901 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1902 return FALSE;
1903 }
1904
1905 // Keep necessary information
1906 Icc ->TagSaveAsRaw[i] = FALSE;
|
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 */
24
25 // This file is available under and governed by the GNU General Public
26 // License version 2 only, as published by the Free Software Foundation.
27 // However, the following notice accompanied the original version of this
28 // file:
29 //
30 //---------------------------------------------------------------------------------
31 //
32 // Little Color Management System
33 // Copyright (c) 1998-2016 Marti Maria Saguer
34 //
35 // Permission is hereby granted, free of charge, to any person obtaining
36 // a copy of this software and associated documentation files (the "Software"),
37 // to deal in the Software without restriction, including without limitation
38 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
39 // and/or sell copies of the Software, and to permit persons to whom the Software
40 // is furnished to do so, subject to the following conditions:
41 //
42 // The above copyright notice and this permission notice shall be included in
43 // all copies or substantial portions of the Software.
44 //
45 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
46 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
47 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
48 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
49 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
50 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
51 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
52 //
53 //---------------------------------------------------------------------------------
336 if (iohandler) _cmsFree(ContextID, iohandler);
337 return NULL;
338 }
339
340 // File-based stream -------------------------------------------------------
341
342 // Read count elements of size bytes each. Return number of elements read
343 static
344 cmsUInt32Number FileRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count)
345 {
346 cmsUInt32Number nReaded = (cmsUInt32Number) fread(Buffer, size, count, (FILE*) iohandler->stream);
347
348 if (nReaded != count) {
349 cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Read error. Got %d bytes, block should be of %d bytes", nReaded * size, count * size);
350 return 0;
351 }
352
353 return nReaded;
354 }
355
356 // Position file pointer in the file
357 static
358 cmsBool FileSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset)
359 {
360 if (fseek((FILE*) iohandler ->stream, (long) offset, SEEK_SET) != 0) {
361
362 cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Seek error; probably corrupted file");
363 return FALSE;
364 }
365
366 return TRUE;
367 }
368
369 // Returns file pointer position
370 static
371 cmsUInt32Number FileTell(cmsIOHANDLER* iohandler)
372 {
373 return (cmsUInt32Number) ftell((FILE*)iohandler ->stream);
374 }
375
376 // Writes data to stream, also keeps used space for further reference. Returns TRUE on success, FALSE on error
380 if (size == 0) return TRUE; // We allow to write 0 bytes, but nothing is written
381
382 iohandler->UsedSpace += size;
383 return (fwrite(Buffer, size, 1, (FILE*) iohandler->stream) == 1);
384 }
385
386 // Closes the file
387 static
388 cmsBool FileClose(cmsIOHANDLER* iohandler)
389 {
390 if (fclose((FILE*) iohandler ->stream) != 0) return FALSE;
391 _cmsFree(iohandler ->ContextID, iohandler);
392 return TRUE;
393 }
394
395 // Create a iohandler for disk based files.
396 cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const char* FileName, const char* AccessMode)
397 {
398 cmsIOHANDLER* iohandler = NULL;
399 FILE* fm = NULL;
400 cmsInt32Number fileLen;
401
402 _cmsAssert(FileName != NULL);
403 _cmsAssert(AccessMode != NULL);
404
405 iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER));
406 if (iohandler == NULL) return NULL;
407
408 switch (*AccessMode) {
409
410 case 'r':
411 fm = fopen(FileName, "rb");
412 if (fm == NULL) {
413 _cmsFree(ContextID, iohandler);
414 cmsSignalError(ContextID, cmsERROR_FILE, "File '%s' not found", FileName);
415 return NULL;
416 }
417 fileLen = cmsfilelength(fm);
418 if (fileLen < 0)
419 {
420 fclose(fm);
421 _cmsFree(ContextID, iohandler);
422 cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of file '%s'", FileName);
423 return NULL;
424 }
425
426 iohandler -> ReportedSize = (cmsUInt32Number) fileLen;
427 break;
428
429 case 'w':
430 fm = fopen(FileName, "wb");
431 if (fm == NULL) {
432 _cmsFree(ContextID, iohandler);
433 cmsSignalError(ContextID, cmsERROR_FILE, "Couldn't create '%s'", FileName);
434 return NULL;
435 }
436 iohandler -> ReportedSize = 0;
437 break;
438
439 default:
440 _cmsFree(ContextID, iohandler);
441 cmsSignalError(ContextID, cmsERROR_FILE, "Unknown access mode '%c'", *AccessMode);
442 return NULL;
443 }
444
445 iohandler ->ContextID = ContextID;
446 iohandler ->stream = (void*) fm;
447 iohandler ->UsedSpace = 0;
448
449 // Keep track of the original file
450 strncpy(iohandler -> PhysicalFile, FileName, sizeof(iohandler -> PhysicalFile)-1);
451 iohandler -> PhysicalFile[sizeof(iohandler -> PhysicalFile)-1] = 0;
452
453 iohandler ->Read = FileRead;
454 iohandler ->Seek = FileSeek;
455 iohandler ->Close = FileClose;
456 iohandler ->Tell = FileTell;
457 iohandler ->Write = FileWrite;
458
459 return iohandler;
460 }
461
462 // Create a iohandler for stream based files
463 cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromStream(cmsContext ContextID, FILE* Stream)
464 {
465 cmsIOHANDLER* iohandler = NULL;
466 cmsInt32Number fileSize;
467
468 fileSize = cmsfilelength(Stream);
469 if (fileSize < 0)
470 {
471 cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of stream");
472 return NULL;
473 }
474
475 iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER));
476 if (iohandler == NULL) return NULL;
477
478 iohandler -> ContextID = ContextID;
479 iohandler -> stream = (void*) Stream;
480 iohandler -> UsedSpace = 0;
481 iohandler -> ReportedSize = (cmsUInt32Number) fileSize;
482 iohandler -> PhysicalFile[0] = 0;
483
484 iohandler ->Read = FileRead;
485 iohandler ->Seek = FileSeek;
486 iohandler ->Close = FileClose;
487 iohandler ->Tell = FileTell;
488 iohandler ->Write = FileWrite;
489
490 return iohandler;
491 }
492
493
494
495 // Close an open IO handler
496 cmsBool CMSEXPORT cmsCloseIOhandler(cmsIOHANDLER* io)
497 {
498 return io -> Close(io);
499 }
500
501 // -------------------------------------------------------------------------------------------------------
653 _cmsDeleteTagByPos(Icc, i);
654 *NewPos = i;
655 }
656 else {
657
658 // No, make a new one
659
660 if (Icc -> TagCount >= MAX_TABLE_TAG) {
661 cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", MAX_TABLE_TAG);
662 return FALSE;
663 }
664
665 *NewPos = Icc ->TagCount;
666 Icc -> TagCount++;
667 }
668
669 return TRUE;
670 }
671
672
673 // Check existence
674 cmsBool CMSEXPORT cmsIsTag(cmsHPROFILE hProfile, cmsTagSignature sig)
675 {
676 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) (void*) hProfile;
677 return _cmsSearchTag(Icc, sig, FALSE) >= 0;
678 }
679
680
681
682 // Enforces that the profile version is per. spec.
683 // Operates on the big endian bytes from the profile.
684 // Called before converting to platform endianness.
685 // Byte 0 is BCD major version, so max 9.
686 // Byte 1 is 2 BCD digits, one per nibble.
687 // Reserved bytes 2 & 3 must be 0.
688 static
689 cmsUInt32Number _validatedVersion(cmsUInt32Number DWord)
690 {
691 cmsUInt8Number* pByte = (cmsUInt8Number*) &DWord;
692 cmsUInt8Number temp1;
693 cmsUInt8Number temp2;
709 {
710 cmsTagEntry Tag;
711 cmsICCHeader Header;
712 cmsUInt32Number i, j;
713 cmsUInt32Number HeaderSize;
714 cmsIOHANDLER* io = Icc ->IOhandler;
715 cmsUInt32Number TagCount;
716
717
718 // Read the header
719 if (io -> Read(io, &Header, sizeof(cmsICCHeader), 1) != 1) {
720 return FALSE;
721 }
722
723 // Validate file as an ICC profile
724 if (_cmsAdjustEndianess32(Header.magic) != cmsMagicNumber) {
725 cmsSignalError(Icc ->ContextID, cmsERROR_BAD_SIGNATURE, "not an ICC profile, invalid signature");
726 return FALSE;
727 }
728
729 // Adjust endianness of the used parameters
730 Icc -> DeviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Header.deviceClass);
731 Icc -> ColorSpace = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.colorSpace);
732 Icc -> PCS = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.pcs);
733
734 Icc -> RenderingIntent = _cmsAdjustEndianess32(Header.renderingIntent);
735 Icc -> flags = _cmsAdjustEndianess32(Header.flags);
736 Icc -> manufacturer = _cmsAdjustEndianess32(Header.manufacturer);
737 Icc -> model = _cmsAdjustEndianess32(Header.model);
738 Icc -> creator = _cmsAdjustEndianess32(Header.creator);
739
740 _cmsAdjustEndianess64(&Icc -> attributes, &Header.attributes);
741 Icc -> Version = _cmsAdjustEndianess32(_validatedVersion(Header.version));
742
743 // Get size as reported in header
744 HeaderSize = _cmsAdjustEndianess32(Header.size);
745
746 // Make sure HeaderSize is lower than profile size
747 if (HeaderSize >= Icc ->IOhandler ->ReportedSize)
748 HeaderSize = Icc ->IOhandler ->ReportedSize;
749
827
828 Header.flags = _cmsAdjustEndianess32(Icc -> flags);
829 Header.manufacturer = _cmsAdjustEndianess32(Icc -> manufacturer);
830 Header.model = _cmsAdjustEndianess32(Icc -> model);
831
832 _cmsAdjustEndianess64(&Header.attributes, &Icc -> attributes);
833
834 // Rendering intent in the header (for embedded profiles)
835 Header.renderingIntent = _cmsAdjustEndianess32(Icc -> RenderingIntent);
836
837 // Illuminant is always D50
838 Header.illuminant.X = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->X));
839 Header.illuminant.Y = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->Y));
840 Header.illuminant.Z = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->Z));
841
842 // Created by LittleCMS (that's me!)
843 Header.creator = _cmsAdjustEndianess32(lcmsSignature);
844
845 memset(&Header.reserved, 0, sizeof(Header.reserved));
846
847 // Set profile ID. Endianness is always big endian
848 memmove(&Header.profileID, &Icc ->ProfileID, 16);
849
850 // Dump the header
851 if (!Icc -> IOhandler->Write(Icc->IOhandler, sizeof(cmsICCHeader), &Header)) return FALSE;
852
853 // Saves Tag directory
854
855 // Get true count
856 for (i=0; i < Icc -> TagCount; i++) {
857 if (Icc ->TagNames[i] != (cmsTagSignature) 0)
858 Count++;
859 }
860
861 // Store number of tags
862 if (!_cmsWriteUInt32Number(Icc ->IOhandler, Count)) return FALSE;
863
864 for (i=0; i < Icc -> TagCount; i++) {
865
866 if (Icc ->TagNames[i] == (cmsTagSignature) 0) continue; // It is just a placeholder
867
868 Tag.sig = (cmsTagSignature) _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagNames[i]);
869 Tag.offset = _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagOffsets[i]);
870 Tag.size = _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagSizes[i]);
871
872 if (!Icc ->IOhandler -> Write(Icc-> IOhandler, sizeof(cmsTagEntry), &Tag)) return FALSE;
873 }
874
875 return TRUE;
876 }
877
878 // ----------------------------------------------------------------------- Set/Get several struct members
879
880
881 cmsUInt32Number CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile)
882 {
883 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
884 return Icc -> RenderingIntent;
885 }
886
1196
1197
1198
1199 // Dump tag contents. If the profile is being modified, untouched tags are copied from FileOrig
1200 static
1201 cmsBool SaveTags(_cmsICCPROFILE* Icc, _cmsICCPROFILE* FileOrig)
1202 {
1203 cmsUInt8Number* Data;
1204 cmsUInt32Number i;
1205 cmsUInt32Number Begin;
1206 cmsIOHANDLER* io = Icc ->IOhandler;
1207 cmsTagDescriptor* TagDescriptor;
1208 cmsTagTypeSignature TypeBase;
1209 cmsTagTypeSignature Type;
1210 cmsTagTypeHandler* TypeHandler;
1211 cmsFloat64Number Version = cmsGetProfileVersion((cmsHPROFILE) Icc);
1212 cmsTagTypeHandler LocalTypeHandler;
1213
1214 for (i=0; i < Icc -> TagCount; i++) {
1215
1216 if (Icc ->TagNames[i] == (cmsTagSignature) 0) continue;
1217
1218 // Linked tags are not written
1219 if (Icc ->TagLinked[i] != (cmsTagSignature) 0) continue;
1220
1221 Icc -> TagOffsets[i] = Begin = io ->UsedSpace;
1222
1223 Data = (cmsUInt8Number*) Icc -> TagPtrs[i];
1224
1225 if (!Data) {
1226
1227 // Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user.
1228 // In this case a blind copy of the block data is performed
1229 if (FileOrig != NULL && Icc -> TagOffsets[i]) {
1230
1231 cmsUInt32Number TagSize = FileOrig -> TagSizes[i];
1232 cmsUInt32Number TagOffset = FileOrig -> TagOffsets[i];
1233 void* Mem;
1234
1235 if (!FileOrig ->IOhandler->Seek(FileOrig ->IOhandler, TagOffset)) return FALSE;
1236
1330
1331 }
1332 }
1333
1334 return TRUE;
1335 }
1336
1337 // Low-level save to IOHANDLER. It returns the number of bytes used to
1338 // store the profile, or zero on error. io may be NULL and in this case
1339 // no data is written--only sizes are calculated
1340 cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOHANDLER* io)
1341 {
1342 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
1343 _cmsICCPROFILE Keep;
1344 cmsIOHANDLER* PrevIO = NULL;
1345 cmsUInt32Number UsedSpace;
1346 cmsContext ContextID;
1347
1348 _cmsAssert(hProfile != NULL);
1349
1350 if (!_cmsLockMutex(Icc->ContextID, Icc->UsrMutex)) return 0;
1351 memmove(&Keep, Icc, sizeof(_cmsICCPROFILE));
1352
1353 ContextID = cmsGetProfileContextID(hProfile);
1354 PrevIO = Icc ->IOhandler = cmsOpenIOhandlerFromNULL(ContextID);
1355 if (PrevIO == NULL) {
1356 _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex);
1357 return 0;
1358 }
1359
1360 // Pass #1 does compute offsets
1361
1362 if (!_cmsWriteHeader(Icc, 0)) goto Error;
1363 if (!SaveTags(Icc, &Keep)) goto Error;
1364
1365 UsedSpace = PrevIO ->UsedSpace;
1366
1367 // Pass #2 does save to iohandler
1368
1369 if (io != NULL) {
1370
1371 Icc ->IOhandler = io;
1372 if (!SetLinks(Icc)) goto Error;
1373 if (!_cmsWriteHeader(Icc, UsedSpace)) goto Error;
1374 if (!SaveTags(Icc, &Keep)) goto Error;
1375 }
1376
1377 memmove(Icc, &Keep, sizeof(_cmsICCPROFILE));
1378 if (!cmsCloseIOhandler(PrevIO))
1379 UsedSpace = 0; // As a error marker
1380
1381 _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex);
1382
1383 return UsedSpace;
1384
1385
1386 Error:
1387 cmsCloseIOhandler(PrevIO);
1388 memmove(Icc, &Keep, sizeof(_cmsICCPROFILE));
1389 _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex);
1390
1391 return 0;
1392 }
1393
1394
1395 // Low-level save to disk.
1396 cmsBool CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName)
1397 {
1398 cmsContext ContextID = cmsGetProfileContextID(hProfile);
1399 cmsIOHANDLER* io = cmsOpenIOhandlerFromFile(ContextID, FileName, "w");
1400 cmsBool rc;
1401
1402 if (io == NULL) return FALSE;
1403
1404 rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0);
1405 rc &= cmsCloseIOhandler(io);
1406
1407 if (rc == FALSE) { // remove() is C99 per 7.19.4.1
1408 remove(FileName); // We have to IGNORE return value in this case
1409 }
1410 return rc;
1574 BaseType = _cmsReadTypeBase(io);
1575 if (BaseType == 0) goto Error;
1576
1577 if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error;
1578
1579 TagSize -= 8; // Alredy read by the type base logic
1580
1581 // Get type handler
1582 TypeHandler = _cmsGetTagTypeHandler(Icc ->ContextID, BaseType);
1583 if (TypeHandler == NULL) goto Error;
1584 LocalTypeHandler = *TypeHandler;
1585
1586
1587 // Read the tag
1588 Icc -> TagTypeHandlers[n] = TypeHandler;
1589
1590 LocalTypeHandler.ContextID = Icc ->ContextID;
1591 LocalTypeHandler.ICCVersion = Icc ->Version;
1592 Icc -> TagPtrs[n] = LocalTypeHandler.ReadPtr(&LocalTypeHandler, io, &ElemCount, TagSize);
1593
1594 // The tag type is supported, but something wrong happened and we cannot read the tag.
1595 // let know the user about this (although it is just a warning)
1596 if (Icc -> TagPtrs[n] == NULL) {
1597
1598 char String[5];
1599
1600 _cmsTagSignature2String(String, sig);
1601 cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted tag '%s'", String);
1602 goto Error;
1603 }
1604
1605 // This is a weird error that may be a symptom of something more serious, the number of
1606 // stored item is actually less than the number of required elements.
1607 if (ElemCount < TagDescriptor ->ElemCount) {
1608
1609 char String[5];
1610
1611 _cmsTagSignature2String(String, sig);
1612 cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "'%s' Inconsistent number of items: expected %d, got %d",
1613 String, TagDescriptor ->ElemCount, ElemCount);
1614 }
1893
1894 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0;
1895
1896 if (!_cmsNewTag(Icc, sig, &i)) {
1897 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1898 return FALSE;
1899 }
1900
1901 // Mark the tag as being written as RAW
1902 Icc ->TagSaveAsRaw[i] = TRUE;
1903 Icc ->TagNames[i] = sig;
1904 Icc ->TagLinked[i] = (cmsTagSignature) 0;
1905
1906 // Keep a copy of the block
1907 Icc ->TagPtrs[i] = _cmsDupMem(Icc ->ContextID, data, Size);
1908 Icc ->TagSizes[i] = Size;
1909
1910 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1911
1912 if (Icc->TagPtrs[i] == NULL) {
1913 Icc->TagNames[i] = (cmsTagSignature) 0;
1914 return FALSE;
1915 }
1916 return TRUE;
1917 }
1918
1919 // Using this function you can collapse several tag entries to the same block in the profile
1920 cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSignature dest)
1921 {
1922 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
1923 int i;
1924
1925 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE;
1926
1927 if (!_cmsNewTag(Icc, sig, &i)) {
1928 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1929 return FALSE;
1930 }
1931
1932 // Keep necessary information
1933 Icc ->TagSaveAsRaw[i] = FALSE;
|