tool to fixes some issues in baseline tiffs (with extensions), see http://andreas-romeyke.de
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

247 lines
11 KiB

  1. /* checks ICC Profile, using Spec http://www.color.org/specification/ICC1v43_2010-12.pdf */
  2. #include "fixit_tiff.h"
  3. #include "check_icc_header.h"
  4. /*
  5. #define USE_WARNING
  6. #ifdef USE_WARNING
  7. #define FAIL(...) {fprintf(stderr, __VA_ARGS__); return 1;};
  8. #define INFO(...) fprintf(stderr,__VA_ARGS__);
  9. #else
  10. #define FAIL(...) {return 1;};
  11. #define INFO(...) ;
  12. #endif
  13. */
  14. #define FAIL(...) {snprintf(errmessage, errsize, __VA_ARGS__); return 1;};
  15. #define INFO(...) ;
  16. // TODO: errormessage pointer und maxsize übergeben
  17. int parse_icc_header_v240_v430(unsigned long iccsize, char * iccdata, unsigned long errsize, char * errmessage) {
  18. assert(iccdata != NULL);
  19. assert(errmessage != NULL);
  20. assert(errsize > 0);
  21. if (iccsize < 128) FAIL("Invalid ICC profile, minimum size is 128 byte");
  22. unsigned long profilesize = (
  23. ((iccdata[0] & 0x00ff) <<24) |
  24. ((iccdata[1] & 0x00ff) <<16) |
  25. ((iccdata[2] & 0x00ff) <<8) |
  26. (iccdata[3] & 0x00ff)
  27. ) & 0xffff;
  28. INFO("ICC: profilesize=%li %0x\n", profilesize, profilesize);
  29. if (profilesize != iccsize) FAIL("committed ICC size (%lu) differs from encoded profilesize (%lu)", iccsize, profilesize);
  30. /* -- */
  31. char preferredcmmtype[5]=" "; memcpy(preferredcmmtype, &iccdata[4],4);
  32. INFO("ICC: preferredcmmtype='%s'\n", preferredcmmtype);
  33. /* TODO: check if this is really correct, because found some TIFFs with
  34. * value 'Lino', handle with care! */
  35. if ( /* see document http://www.color.org/registry/signature/TagRegistry-2016-05.pdf */
  36. ('\0' != preferredcmmtype[0]) &&
  37. (0 != strncmp("ADBE", preferredcmmtype, 4)) &&
  38. (0 != strncmp("ACMS", preferredcmmtype, 4)) &&
  39. (0 != strncmp("appl", preferredcmmtype, 4)) &&
  40. (0 != strncmp("CCMS", preferredcmmtype, 4)) &&
  41. (0 != strncmp("UCCM", preferredcmmtype, 4)) &&
  42. (0 != strncmp("UCMS", preferredcmmtype, 4)) &&
  43. (0 != strncmp("EFI ", preferredcmmtype, 4)) &&
  44. (0 != strncmp("FF ", preferredcmmtype, 4)) &&
  45. (0 != strncmp("EXAC", preferredcmmtype, 4)) &&
  46. (0 != strncmp("HCMM", preferredcmmtype, 4)) &&
  47. (0 != strncmp("argl", preferredcmmtype, 4)) &&
  48. (0 != strncmp("LgoS", preferredcmmtype, 4)) &&
  49. (0 != strncmp("HDM ", preferredcmmtype, 4)) &&
  50. (0 != strncmp("lcms", preferredcmmtype, 4)) &&
  51. (0 != strncmp("KCMS", preferredcmmtype, 4)) &&
  52. (0 != strncmp("MCML", preferredcmmtype, 4)) &&
  53. (0 != strncmp("WCS ", preferredcmmtype, 4)) &&
  54. (0 != strncmp("SIGN", preferredcmmtype, 4)) &&
  55. (0 != strncmp("RGMS", preferredcmmtype, 4)) &&
  56. (0 != strncmp("SICC", preferredcmmtype, 4)) &&
  57. (0 != strncmp("TCMM", preferredcmmtype, 4)) &&
  58. (0 != strncmp("32BT", preferredcmmtype, 4)) &&
  59. (0 != strncmp("WTG ", preferredcmmtype, 4)) &&
  60. (0 != strncmp("zc00", preferredcmmtype, 4))
  61. ) FAIL("preferred cmmtype ('%s') should be empty or (possibly, because ICC validation is alpha code) one of following strings: 'ADBE' 'ACMS' 'appl' 'CCMS' 'UCCM' 'UCMS' 'EFI ' 'FF ' 'EXAC' 'HCMM' 'argl' 'LgoS' 'HDM ' 'lcms' 'KCMS' 'MCML' 'WCS ' 'SIGN' 'RGMS' 'SICC' 'TCMM' '32BT' 'WTG ' 'zc00'", preferredcmmtype);
  62. /* -- */
  63. char profileversion[6]=" "; snprintf(profileversion, 6, "%i.%i.%i", (iccdata[8]) & 0x000f, ((iccdata[9] & 0x00f0) >>4), (iccdata[9] & 0x000f));
  64. INFO("ICC: profileversion='%s'\n", profileversion);
  65. // TODO: 2.4.0 or 4.3.0, others not allowed?
  66. if (
  67. (0 != strncmp("2.4.0", profileversion, 5)) &&
  68. (0 != strncmp("4.3.0", profileversion, 5))
  69. ) FAIL("profile version ('%s') should be '2.4.0' or 4.3.0", profileversion);
  70. /* -- */
  71. char profileclass[5]=" "; memcpy(profileclass, &iccdata[12],4);
  72. INFO("ICC: profileclass='%s'\n", profileclass);
  73. if ( /* see page 13, table 11 of http://www.color.org/ICC_Minor_Revision_for_Web.pdf */
  74. (0 != strncmp("scnr", profileclass, 4)) &&
  75. (0 != strncmp("mntr", profileclass, 4)) &&
  76. (0 != strncmp("prtr", profileclass, 4)) &&
  77. (0 != strncmp("link", profileclass, 4)) &&
  78. (0 != strncmp("spac", profileclass, 4)) &&
  79. (0 != strncmp("abst", profileclass, 4)) &&
  80. (0 != strncmp("nmcl", profileclass, 4))
  81. ) FAIL("profile class ('%s'), should be one of following strings for device classes: 'scnr', 'mntr', 'prtr' or for profile classes: 'link', 'spac', 'abst', 'nmcl'", profileclass);
  82. /* -- */
  83. char colorspacedata[5]=" "; memcpy(colorspacedata, &iccdata[16],4);
  84. INFO("ICC: colorspacedata='%s'\n", colorspacedata);
  85. if ( /* see page 14, table 13 of http://www.color.org/ICC_Minor_Revision_for_Web.pdf */
  86. (0 != strncmp("XYZ ", colorspacedata, 4)) &&
  87. (0 != strncmp("Lab ", colorspacedata, 4)) &&
  88. (0 != strncmp("Luv ", colorspacedata, 4)) &&
  89. (0 != strncmp("YCbr", colorspacedata, 4)) &&
  90. (0 != strncmp("Yvx ", colorspacedata, 4)) &&
  91. (0 != strncmp("RGB ", colorspacedata, 4)) &&
  92. (0 != strncmp("GRAY", colorspacedata, 4)) &&
  93. (0 != strncmp("HSV ", colorspacedata, 4)) &&
  94. (0 != strncmp("HLS ", colorspacedata, 4)) &&
  95. (0 != strncmp("CMYK", colorspacedata, 4)) &&
  96. (0 != strncmp("CMY ", colorspacedata, 4)) &&
  97. (0 != strncmp("2CLR", colorspacedata, 4)) &&
  98. (0 != strncmp("3CLR", colorspacedata, 4)) &&
  99. (0 != strncmp("4CLR", colorspacedata, 4)) &&
  100. (0 != strncmp("5CLR", colorspacedata, 4)) &&
  101. (0 != strncmp("6CLR", colorspacedata, 4)) &&
  102. (0 != strncmp("7CLR", colorspacedata, 4)) &&
  103. (0 != strncmp("8CLR", colorspacedata, 4)) &&
  104. (0 != strncmp("9CLR", colorspacedata, 4)) &&
  105. (0 != strncmp("ACLR", colorspacedata, 4)) &&
  106. (0 != strncmp("BCLR", colorspacedata, 4)) &&
  107. (0 != strncmp("CCLR", colorspacedata, 4)) &&
  108. (0 != strncmp("DCLR", colorspacedata, 4)) &&
  109. (0 != strncmp("ECLR", colorspacedata, 4)) &&
  110. (0 != strncmp("FCLR", colorspacedata, 4))
  111. ) FAIL("colorspace data ('%s'), should be one of following strings: 'XYZ ' 'Lab ' 'Luv ' 'YCbr' 'Yvx ' 'RGB ' 'GRAY' 'HSV ' 'HLS ' 'CMYK' 'CMY ' '2CLR' '3CLR' '4CLR' '5CLR' '6CLR' '7CLR' '8CLR' '9CLR' 'ACLR' 'BCLR' 'CCLR' 'DCLR' 'ECLR' 'FCLR'", colorspacedata);
  112. /* -- */
  113. char connectionspacedata[5]=" "; memcpy(connectionspacedata, &iccdata[20],4);
  114. INFO("ICC: connectionspacedata(PCS)='%s'\n", connectionspacedata);
  115. if ( /* see page 15, table 14 of http://www.color.org/ICC_Minor_Revision_for_Web.pdf */
  116. (0 != strncmp("XYZ ", connectionspacedata, 4)) &&
  117. (0 != strncmp("Lab ", connectionspacedata, 4))
  118. ) FAIL("connection space data ('%s') should be one of following strings: 'XYZ ' 'Lab '", connectionspacedata);
  119. /* -- */
  120. // datetime 24-35
  121. char datetime[20]; snprintf(datetime, 20, "%.4d:%.2d:%.2d %.2d:%.2d:%.2d",
  122. ((iccdata[24] & 0x00ff)<<8 | (iccdata[25] & 0x00ff)), // yyyy
  123. ((iccdata[26] & 0x00ff)<<8 | (iccdata[27] & 0x00ff)), // MM
  124. ((iccdata[28] & 0x00ff)<<8 | (iccdata[29] & 0x00ff)), // DD
  125. ((iccdata[30] & 0x00ff)<<8 | (iccdata[31] & 0x00ff)), // hh
  126. ((iccdata[32] & 0x00ff)<<8 | (iccdata[33] & 0x00ff)), // mm
  127. ((iccdata[34] & 0x00ff)<<8 | (iccdata[35] & 0x00ff)) // ss
  128. );
  129. INFO("ICC: datetime='%s'\n", datetime);
  130. /* -- */
  131. char profilefilesignature[5]=" "; memcpy(profilefilesignature, &iccdata[36],4);
  132. INFO("ICC: profilefilesignature='%s'\n", profilefilesignature);
  133. /* -- */
  134. char primaryplattformsignature[5]=" "; memcpy(primaryplattformsignature, &iccdata[40],4);
  135. INFO("ICC: primaryplattformsignature='%s'\n", primaryplattformsignature);
  136. if ( /* see page 15, table 15 of http://www.color.org/ICC_Minor_Revision_for_Web.pdf */
  137. ('\0' != primaryplattformsignature[0]) &&
  138. (0 != strncmp("APPL", primaryplattformsignature, 4)) &&
  139. (0 != strncmp("MSFT", primaryplattformsignature, 4)) &&
  140. (0 != strncmp("SGI ", primaryplattformsignature, 4)) &&
  141. (0 != strncmp("SUNW", primaryplattformsignature, 4)) &&
  142. (0 != strncmp("TGNT", primaryplattformsignature, 4))
  143. ) FAIL("primary plattform signature ('%s') should be empty or one of following strings: 'APPL', 'MSFT', 'SGI ', 'SUNW', 'TGNT'", primaryplattformsignature);
  144. /* -- */
  145. // Profile Flags 44-47
  146. /* -- */
  147. char devicemanufacturer[5]=" "; memcpy(devicemanufacturer, &iccdata[48],4);
  148. INFO("ICC: devicemanufacturer='%s'\n", devicemanufacturer);
  149. /* -- */
  150. char devicemodel[5]=" "; memcpy(devicemodel, &iccdata[52],4);
  151. INFO("ICC: devicemodel='%s'\n", devicemodel);
  152. /* -- */
  153. // device attributes 56-63
  154. /* -- */
  155. // rendering intent field 64-67
  156. /* -- */
  157. return 0;
  158. }
  159. /* checks a ICC header, see chapter 7 of http://www.color.org/specification/ICC1v43_2010-12.pdf */
  160. int parse_icc_v430(unsigned long iccsize, char* iccdata, unsigned long errsize, char * errmessage) {
  161. assert(iccdata != NULL);
  162. assert(errmessage != NULL);
  163. assert(errsize > 0);
  164. if (iccsize < 128) FAIL ("Invalid ICC profile 1v43_2010, see http://www.color.org/specification/ICC1v43_2010-12.pdf for details");
  165. int ret_header = parse_icc_header_v240_v430( iccsize, iccdata, errsize, errmessage);
  166. if (0 != ret_header) return ret_header;
  167. else {
  168. // PCS illuminant field 68-79
  169. /* -- */
  170. // Profile creator field 80-83
  171. /* -- */
  172. // Profile ID field 84-99
  173. /* -- */
  174. // Reserved field bytes 100-127
  175. };
  176. return 0;
  177. }
  178. /* checks a ICC header, see chapter 6 of http://www.color.org/ICC_Minor_Revision_for_Web.pdf */
  179. int parse_icc_v240(unsigned long iccsize, char* iccdata, unsigned long errsize, char * errmessage) {
  180. assert(iccdata != NULL);
  181. assert(errmessage != NULL);
  182. assert(errsize > 0);
  183. if (iccsize < 128) FAIL ("Invalid ICC profile ICC.1:2001-04, see http://www.color.org/ICC_Minor_Revision_for_Web.pdf for details");
  184. int ret_header = parse_icc_header_v240_v430( iccsize, iccdata, errsize, errmessage);
  185. if (0 != ret_header) return ret_header;
  186. else {
  187. // PCS xyz illuminant 68-79
  188. /* -- */
  189. // Profile creator signature 80-83
  190. /* -- */
  191. // Reserved field bytes 84-127
  192. };
  193. return 0;
  194. }
  195. /* returns 0 if valid, !0 if errorneous */
  196. int parse_icc(unsigned long iccsize, char* iccdata, unsigned long errsize, char * errmessage) {
  197. assert(iccdata != NULL);
  198. assert(errmessage != NULL);
  199. assert(errsize > 0);
  200. if (iccsize < 10) FAIL ("Invalid ICC profile");
  201. char profileversion[6]=" "; snprintf(profileversion, 6, "%i.%i.%i", (iccdata[8]) & 0x000f, ((iccdata[9] & 0x00f0) >>4), (iccdata[9] & 0x000f));
  202. INFO("ICC: profileversion='%s'\n", profileversion);
  203. if (0==strncmp(profileversion, "4.3.0",5)) return parse_icc_v430(iccsize, iccdata, errsize, errmessage);
  204. else if (0==strncmp(profileversion, "2.4.0",5)) return parse_icc_v240(iccsize,iccdata, errsize, errmessage);
  205. else {
  206. return parse_icc_header_v240_v430(iccsize,iccdata, errsize, errmessage);
  207. }
  208. return 0;
  209. }
  210. /* checks ICC header */
  211. int check_icc_header (const char * filename ) {
  212. /* load file */
  213. TIFF* tif = TIFFOpen(filename, "r+");
  214. if (NULL == tif) {
  215. fprintf( stderr, "file '%s' could not be opened\n", filename);
  216. exit (FIXIT_TIFF_READ_PERMISSION_ERROR);
  217. };
  218. /* find icc-tag and fix it */
  219. char *iccprofile=NULL;
  220. uint32 count=0;
  221. int returncode = 0;
  222. int found=TIFFGetField(tif, TIFFTAG_ICCPROFILE, &count, &iccprofile);
  223. if (1==found) { /* there exists a ICC profile field */
  224. unsigned long errsize=1024;
  225. char errmsg[errsize];
  226. returncode = parse_icc(count, iccprofile, errsize, errmsg);
  227. } else if (0 == found) {
  228. if (FLAGGED == flag_be_verbose) printf ("no ICC profile found!\n");
  229. }
  230. TIFFClose(tif);
  231. if (0 == returncode) return FIXIT_TIFF_IS_VALID;
  232. else return FIXIT_TIFF_IS_INVALID;
  233. }