File indexing completed on 2024-05-12 15:59:40
0001 /* 0002 * SPDX-FileCopyrightText: 2009 Boudewijn Rempt <boud@valdyas.org> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 #include "psd_resource_block.h" 0007 0008 #include <QBuffer> 0009 #include <QDataStream> 0010 #include <QIODevice> 0011 0012 #include <kis_debug.h> 0013 0014 #include "psd.h" 0015 #include "psd_resource_section.h" 0016 #include "psd_utils.h" 0017 0018 PSDResourceBlock::PSDResourceBlock() 0019 : KisAnnotation("PSD Resource Block", "", QByteArray()) 0020 , identifier(PSDImageResourceSection::UNKNOWN) 0021 , dataSize(0) 0022 , resource(0) 0023 { 0024 } 0025 0026 bool PSDResourceBlock::read(QIODevice &io) 0027 { 0028 dbgFile << "Reading resource block"; 0029 if (io.atEnd()) { 0030 error = "Could not read resource block: no bytes left."; 0031 return false; 0032 } 0033 0034 QByteArray b; 0035 b = io.read(4); 0036 if (b.size() != 4 || QString(b) != "8BIM") { 0037 error = QString("Could not read resource block signature. Got %1.").arg(QString(b)); 0038 return false; 0039 } 0040 0041 if (!psdread(io, identifier)) { 0042 error = "Could not read resource block identifier"; 0043 return false; 0044 } 0045 0046 dbgFile << "\tresource block identifier" << PSDImageResourceSection::idToString((PSDImageResourceSection::PSDResourceID)identifier) << identifier; 0047 0048 m_type = QString("PSD Resource Block: %1").arg(identifier); 0049 0050 if (!psdread_pascalstring(io, name, 2)) { 0051 error = "Could not read name of resource block"; 0052 return false; 0053 } 0054 0055 dbgFile << "\tresource block name" << name; 0056 0057 if (!psdread(io, dataSize)) { 0058 error = QString("Could not read datasize for resource block with name %1 of type %2").arg(name).arg(identifier); 0059 return false; 0060 } 0061 0062 if ((dataSize & 0x01) != 0) { 0063 dataSize++; 0064 } 0065 0066 dbgFile << "\tresource block size" << dataSize; 0067 0068 m_description = PSDImageResourceSection::idToString((PSDImageResourceSection::PSDResourceID)identifier); 0069 0070 data = io.read(dataSize); 0071 if (data.size() != (int)dataSize) { 0072 error = QString("Could not read data for resource block with name %1 of type %2").arg(name).arg(identifier); 0073 return false; 0074 } 0075 0076 m_annotation = data; 0077 0078 switch (identifier) { 0079 // case PSDImageResourceSection::MAC_PRINT_INFO: 0080 // resource = new MAC_PRINT_INFO_1001; 0081 // break; 0082 case PSDImageResourceSection::RESN_INFO: 0083 resource = new RESN_INFO_1005; 0084 break; 0085 // case PSDImageResourceSection::ALPHA_NAMES: 0086 // resource = new ALPHA_NAMES_1006; 0087 // break; 0088 // case PSDImageResourceSection::DISPLAY_INFO: 0089 // resource = new DISPLAY_INFO_1007; 0090 // break; 0091 // case PSDImageResourceSection::CAPTION: 0092 // resource = new CAPTION_1008; 0093 // break; 0094 // case PSDImageResourceSection::BORDER_INFO: 0095 // resource = new BORDER_INFO_1009; 0096 // break; 0097 // case PSDImageResourceSection::BACKGROUND_COL: 0098 // resource = new BACKGROUND_COL_1010; 0099 // break; 0100 // case PSDImageResourceSection::PRINT_FLAGS: 0101 // resource = new PRINT_FLAGS_1011; 0102 // break; 0103 // case PSDImageResourceSection::GREY_HALFTONE: 0104 // resource = new GREY_HALFTONE_1012; 0105 // break; 0106 // case PSDImageResourceSection::COLOR_HALFTONE: 0107 // resource = new COLOR_HALFTONE_1013; 0108 // break; 0109 // case PSDImageResourceSection::DUOTONE_HALFTONE: 0110 // resource = new DUOTONE_HALFTONE_1014; 0111 // break; 0112 // case PSDImageResourceSection::GREY_XFER: 0113 // resource = new GREY_XFER_1015; 0114 // break; 0115 // case PSDImageResourceSection::COLOR_XFER: 0116 // resource = new COLOR_XFER_1016; 0117 // break; 0118 // case PSDImageResourceSection::DUOTONE_XFER: 0119 // resource = new DUOTONE_XFER_1017; 0120 // break; 0121 // case PSDImageResourceSection::DUOTONE_INFO: 0122 // resource = new DUOTONE_INFO_1018; 0123 // break; 0124 // case PSDImageResourceSection::EFFECTIVE_BW: 0125 // resource = new EFFECTIVE_BW_1019; 0126 // break; 0127 // case PSDImageResourceSection::EPS_OPT: 0128 // resource = new EPS_OPT_1021; 0129 // break; 0130 // case PSDImageResourceSection::QUICK_MASK: 0131 // resource = new QUICK_MASK_1022; 0132 // break; 0133 // case PSDImageResourceSection::LAYER_STATE: 0134 // resource = new LAYER_STATE_1024; 0135 // break; 0136 // case PSDImageResourceSection::WORKING_PATH: 0137 // resource = new WORKING_PATH_1025; 0138 // break; 0139 // case PSDImageResourceSection::LAYER_GROUP: 0140 // resource = new LAYER_GROUP_1026; 0141 // break; 0142 // case PSDImageResourceSection::IPTC_NAA_DATA: 0143 // resource = new IPTC_NAA_DATA_1028; 0144 // break; 0145 // case PSDImageResourceSection::IMAGE_MODE_RAW: 0146 // resource = new IMAGE_MODE_RAW_1029; 0147 // break; 0148 // case PSDImageResourceSection::JPEG_QUAL: 0149 // resource = new JPEG_QUAL_1030; 0150 // break; 0151 // case PSDImageResourceSection::GRID_GUIDE: 0152 // resource = new GRID_GUIDE_1032; 0153 // break; 0154 // case PSDImageResourceSection::THUMB_RES: 0155 // resource = new THUMB_RES_1033; 0156 // break; 0157 // case PSDImageResourceSection::COPYRIGHT_FLG: 0158 // resource = new COPYRIGHT_FLG_1034; 0159 // break; 0160 // case PSDImageResourceSection::URL: 0161 // resource = new URL_1035; 0162 // break; 0163 // case PSDImageResourceSection::THUMB_RES2: 0164 // resource = new THUMB_RES2_1036; 0165 // break; 0166 case PSDImageResourceSection::GLOBAL_ANGLE: 0167 resource = new GLOBAL_ANGLE_1037; 0168 break; 0169 // case PSDImageResourceSection::COLOR_SAMPLER: 0170 // resource = new COLOR_SAMPLER_1038; 0171 // break; 0172 case PSDImageResourceSection::ICC_PROFILE: 0173 resource = new ICC_PROFILE_1039; 0174 break; 0175 // case PSDImageResourceSection::WATERMARK: 0176 // resource = new WATERMARK_1040; 0177 // break; 0178 // case PSDImageResourceSection::ICC_UNTAGGED: 0179 // resource = new ICC_UNTAGGED_1041; 0180 // break; 0181 // case PSDImageResourceSection::EFFECTS_VISIBLE: 0182 // resource = new EFFECTS_VISIBLE_1042; 0183 // break; 0184 // case PSDImageResourceSection::SPOT_HALFTONE: 0185 // resource = new SPOT_HALFTONE_1043; 0186 // break; 0187 // case PSDImageResourceSection::DOC_IDS: 0188 // resource = new DOC_IDS_1044; 0189 // break; 0190 // case PSDImageResourceSection::ALPHA_NAMES_UNI: 0191 // resource = new ALPHA_NAMES_UNI_1045; 0192 // break; 0193 // case PSDImageResourceSection::IDX_COL_TAB_CNT: 0194 // resource = new IDX_COL_TAB_CNT_1046; 0195 // break; 0196 // case PSDImageResourceSection::IDX_TRANSPARENT: 0197 // resource = new IDX_TRANSPARENT_1047; 0198 // break; 0199 case PSDImageResourceSection::GLOBAL_ALT: 0200 resource = new GLOBAL_ALT_1049; 0201 break; 0202 // case PSDImageResourceSection::SLICES: 0203 // resource = new SLICES_1050; 0204 // break; 0205 // case PSDImageResourceSection::WORKFLOW_URL_UNI: 0206 // resource = new WORKFLOW_URL_UNI_1051; 0207 // break; 0208 // case PSDImageResourceSection::JUMP_TO_XPEP: 0209 // resource = new JUMP_TO_XPEP_1052; 0210 // break; 0211 // case PSDImageResourceSection::ALPHA_ID: 0212 // resource = new ALPHA_ID_1053; 0213 // break; 0214 // case PSDImageResourceSection::URL_LIST_UNI: 0215 // resource = new URL_LIST_UNI_1054; 0216 // break; 0217 // case PSDImageResourceSection::VERSION_INFO: 0218 // resource = new VERSION_INFO_1057; 0219 // break; 0220 // case PSDImageResourceSection::EXIF_DATA: 0221 // resource = new EXIF_DATA_1058; 0222 // break; 0223 // case PSDImageResourceSection::XMP_DATA: 0224 // resource = new XMP_DATA_1060; 0225 // break; 0226 // case PSDImageResourceSection::PATH_INFO_FIRST: 0227 // resource = new PATH_INFO_FIRST_2000; 0228 // break; 0229 // case PSDImageResourceSection::PATH_INFO_LAST: 0230 // resource = new PATH_INFO_LAST_2998; 0231 // break; 0232 // case PSDImageResourceSection::CLIPPING_PATH: 0233 // resource = new CLIPPING_PATH_2999; 0234 // break; 0235 // case PSDImageResourceSection::PRINT_FLAGS_2: 0236 // resource = new PRINT_FLAGS_2_10000; 0237 // break; 0238 default:; 0239 } 0240 0241 if (resource) { 0242 resource->interpretBlock(data); 0243 } 0244 0245 return valid(); 0246 } 0247 0248 bool PSDResourceBlock::write(QIODevice &io) const 0249 { 0250 dbgFile << "Writing Resource Block" << PSDImageResourceSection::idToString((PSDImageResourceSection::PSDResourceID)identifier) << identifier; 0251 0252 if (resource && !resource->valid()) { 0253 error = QString("Cannot write an invalid Resource Block"); 0254 return false; 0255 } 0256 0257 if (identifier == PSDImageResourceSection::LAYER_STATE || identifier == PSDImageResourceSection::LAYER_GROUP 0258 || identifier == PSDImageResourceSection::LAYER_COMPS || identifier == PSDImageResourceSection::LAYER_GROUP_ENABLED_ID 0259 || identifier == PSDImageResourceSection::LAYER_SELECTION_ID) { 0260 /** 0261 * We can actually handle LAYER_SELECTION_ID. It consists 0262 * of a number of layers and a list of IDs to select, which 0263 * are retrieved from 'lyid' additional layer block. 0264 */ 0265 dbgFile << "Skip writing resource block" << identifier << displayText(); 0266 return true; 0267 } 0268 0269 QByteArray ba; 0270 0271 // createBlock returns true by default but does not change the data. 0272 if (resource && !resource->createBlock(ba)) { 0273 error = resource->error; 0274 return false; 0275 } else if (!resource) { 0276 // reconstruct from the data 0277 QBuffer buf(&ba); 0278 buf.open(QBuffer::WriteOnly); 0279 psdwrite(buf, "8BIM"); 0280 psdwrite(buf, identifier); 0281 psdwrite_pascalstring(buf, name); 0282 psdwrite(buf, dataSize); 0283 buf.write(data); 0284 buf.close(); 0285 } 0286 if (io.write(ba.constData(), ba.size()) != ba.size()) { 0287 error = QString("Could not write complete resource"); 0288 return false; 0289 } 0290 0291 return true; 0292 } 0293 0294 bool PSDResourceBlock::valid() 0295 { 0296 if (identifier == PSDImageResourceSection::UNKNOWN) { 0297 error = QString("Unknown ID: %1").arg(identifier); 0298 return false; 0299 } 0300 if (data.size() != (int)dataSize) { 0301 error = QString("Needed %1 bytes, got %2 bytes of data").arg(dataSize).arg(data.length()); 0302 return false; 0303 } 0304 return true; 0305 } 0306 0307 bool RESN_INFO_1005::interpretBlock(QByteArray data) 0308 { 0309 dbgFile << "Reading RESN_INFO_1005"; 0310 0311 // the resolution we set on the image should be dpi; we can also set the unit on the KisDocument. 0312 QDataStream ds(data); 0313 ds.setByteOrder(QDataStream::BigEndian); 0314 0315 ds >> hRes >> hResUnit >> widthUnit >> vRes >> vResUnit >> heightUnit; 0316 0317 /* Resolution always recorded as pixels / inch in a fixed point implied 0318 decimal int32 with 16 bits before point and 16 after (i.e. cast as 0319 double and divide resolution by 2^16 */ 0320 dbgFile << "hres" << hRes << "vres" << vRes; 0321 0322 hRes = hRes / 65536.0; 0323 vRes = vRes / 65536.0; 0324 0325 dbgFile << hRes << hResUnit << widthUnit << vRes << vResUnit << heightUnit; 0326 0327 return ds.atEnd(); 0328 } 0329 0330 bool RESN_INFO_1005::createBlock(QByteArray &data) 0331 { 0332 dbgFile << "Writing RESN_INFO_1005"; 0333 QBuffer buf(&data); 0334 0335 startBlock(buf, PSDImageResourceSection::RESN_INFO, 16); 0336 0337 // Convert to 16.16 fixed point 0338 Fixed h = hRes * 65536.0 + 0.5; 0339 dbgFile << "h" << h << "hRes" << hRes; 0340 psdwrite(buf, h); 0341 psdwrite(buf, hResUnit); 0342 psdwrite(buf, widthUnit); 0343 0344 // Convert to 16.16 fixed point 0345 Fixed v = vRes * 65536.0 + 0.5; 0346 dbgFile << "v" << v << "vRes" << vRes; 0347 psdwrite(buf, v); 0348 psdwrite(buf, vResUnit); 0349 psdwrite(buf, heightUnit); 0350 0351 buf.close(); 0352 0353 return true; 0354 } 0355 0356 bool ICC_PROFILE_1039::interpretBlock(QByteArray data) 0357 { 0358 dbgFile << "Reading ICC_PROFILE_1039"; 0359 0360 icc = data; 0361 0362 return true; 0363 } 0364 0365 bool ICC_PROFILE_1039::createBlock(QByteArray &data) 0366 { 0367 dbgFile << "Writing ICC_PROFILE_1039"; 0368 if (icc.size() == 0) { 0369 error = "ICC_PROFILE_1039: Trying to save an empty profile"; 0370 return false; 0371 } 0372 QBuffer buf(&data); 0373 startBlock(buf, PSDImageResourceSection::ICC_PROFILE, icc.size()); 0374 buf.write(icc.constData(), icc.size()); 0375 buf.close(); 0376 0377 return true; 0378 }