/* This file is part of "psdparse" Copyright (C) 2004-9 Toby Thain, toby@telegraphics.com.au This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "psdparse.h" char dirsep[] = {DIRSEP,0}; FILE *listfile = NULL, *xml = NULL; void skipblock(psd_file_t f, char *desc){ psd_bytes_t n = get4B(f); if(n){ fseeko(f, n, SEEK_CUR); VERBOSE(" ...skipped %s (" LL_L("%lld","%ld") " bytes)\n", desc, n); }else VERBOSE(" (%s is empty)\n", desc); } /** * Process the "layer and mask information" section. The file must be * positioned at the beginning of this section when dolayermaskinfo() * is called. * * The function sets h->nlayers to the number of layers in the PSD, * and also populates the h->linfo[] array. */ void readlayerinfo(FILE *f, struct psd_header *h, int i) { psd_bytes_t chlen, extralen, extrastart; int j, chid, namelen; char *chidstr, tmp[10]; struct layer_info *li = h->linfo + i; // process layer record li->top = get4B(f); li->left = get4B(f); li->bottom = get4B(f); li->right = get4B(f); li->channels = get2Bu(f); VERBOSE("\n"); UNQUIET(" layer %d: (%4ld,%4ld,%4ld,%4ld), %d channels (%4ld rows x %4ld cols)\n", i, li->top, li->left, li->bottom, li->right, li->channels, li->bottom-li->top, li->right-li->left); if( li->bottom < li->top || li->right < li->left || li->channels > 64 ) // sanity check { alwayswarn("### something's not right about that, trying to skip layer.\n"); fseeko(f, 6*li->channels+12, SEEK_CUR); skipblock(f, "layer info: extra data"); } else { li->chan = checkmalloc(li->channels*sizeof(struct channel_info)); li->chindex = checkmalloc((li->channels+2)*sizeof(int)); li->chindex += 2; // so we can index array from [-2] (hackish) for(j = -2; j < li->channels; ++j) li->chindex[j] = -1; // fetch info on each of the layer's channels for(j = 0; j < li->channels; ++j){ chid = li->chan[j].id = get2B(f); chlen = li->chan[j].length = GETPSDBYTES(f); if(chid >= -2 && chid < li->channels) li->chindex[chid] = j; else warn_msg("unexpected channel id %d", chid); switch(chid){ case -2: chidstr = " (layer mask)"; break; case -1: chidstr = " (transparency mask)"; break; default: if(h->mode != SCAVENGE_MODE && chid < (int)strlen(channelsuffixes[h->mode])) sprintf(chidstr = tmp, " (%c)", channelsuffixes[h->mode][chid]); // it's a mode-ish channel else chidstr = ""; // can't explain it } VERBOSE(" channel %2d: " LL_L("%7lld","%7ld") " bytes, id=%2d %s\n", j, chlen, chid, chidstr); } fread(li->blend.sig, 1, 4, f); fread(li->blend.key, 1, 4, f); li->blend.opacity = fgetc(f); li->blend.clipping = fgetc(f); li->blend.flags = fgetc(f); fgetc(f); // padding // process layer's 'extra data' section extralen = get4B(f); extrastart = ftello(f); VERBOSE(" (extra data: " LL_L("%lld","%ld") " bytes @ " LL_L("%lld","%ld") ")\n", extralen, extrastart); // fetch layer mask data if( (li->mask.size = get4B(f)) ){ li->mask.top = get4B(f); li->mask.left = get4B(f); li->mask.bottom = get4B(f); li->mask.right = get4B(f); li->mask.default_colour = fgetc(f); li->mask.flags = fgetc(f); fseeko(f, li->mask.size-18, SEEK_CUR); // skip remainder li->mask.rows = li->mask.bottom - li->mask.top; li->mask.cols = li->mask.right - li->mask.left; VERBOSE(" (has layer mask)\n"); }else VERBOSE(" (no layer mask)\n"); skipblock(f, "layer blending ranges"); // layer name li->nameno = malloc(16); sprintf(li->nameno, "layer%d", i+1); namelen = fgetc(f); li->name = checkmalloc(PAD4(namelen+1)); fread(li->name, 1, PAD4(namelen+1)-1, f); li->name[namelen] = 0; if(namelen){ UNQUIET(" name: \"%s\"\n", li->name); if(li->name[0] == '.') li->name[0] = '_'; } // process layer's 'additional info' li->additionalpos = ftello(f); li->additionallen = extrastart + extralen - li->additionalpos; // leave file positioned after extra data fseeko(f, extrastart + extralen, SEEK_SET); } } void dolayerinfo(psd_file_t f, struct psd_header *h){ int i; // layers structure h->nlayers = get2B(f); h->mergedalpha = h->nlayers < 0; if(h->mergedalpha){ h->nlayers = - h->nlayers; VERBOSE(" (first alpha is transparency for merged image)\n"); } UNQUIET("\n%d layers:\n", h->nlayers); //if( h->nlayers*(18+6*h->channels) > layerlen ){ // sanity check // alwayswarn("### unlikely number of layers, giving up.\n"); // return; //} h->linfo = checkmalloc(h->nlayers*sizeof(struct layer_info)); // load linfo[] array with each layer's info for(i = 0; i < h->nlayers; ++i) readlayerinfo(f, h, i); } void dolayermaskinfo(psd_file_t f, struct psd_header *h){ psd_bytes_t layerlen; h->nlayers = 0; h->lmilen = GETPSDBYTES(f); h->lmistart = ftello(f); if(h->lmilen){ // process layer info section layerlen = GETPSDBYTES(f); if(layerlen){ dolayerinfo(f, h); // after processing all layers, file should now positioned at image data }else VERBOSE(" (layer info section is empty)\n"); }else VERBOSE(" (layer & mask info section is empty)\n"); } /** * Loop over all layers described by layer info section, * spit out a line in asset list if requested, and call * doimage() to process its image data. */ void processlayers(psd_file_t f, struct psd_header *h) { int i; psd_bytes_t savepos; if(listfile) fputs("assetlist = {\n", listfile); for(i = 0; i < h->nlayers; ++i){ struct layer_info *li = &h->linfo[i]; psd_pixels_t cols = li->right - li->left, rows = li->bottom - li->top; VERBOSE("\n layer %d (\"%s\"):\n", i, li->name); if(listfile && cols && rows){ if(numbered) fprintf(listfile, "\t\"%s\" = { pos={%4ld,%4ld}, size={%4ld,%4ld} }, -- %s\n", li->nameno, li->left, li->top, cols, rows, li->name); else fprintf(listfile, "\t\"%s\" = { pos={%4ld,%4ld}, size={%4ld,%4ld} },\n", li->name, li->left, li->top, cols, rows); } if(xml){ fputs("\t\n", li->top, li->left, li->bottom, li->right, cols, rows); } layerblendmode(f, 2, 1, &li->blend); doimage(f, li, numbered ? li->nameno : li->name, h); if(extra && xml){ // Process 'additional data' (non-image layer data, // such as adjustments, effects, type tool). // This pass is purely for XML output. savepos = ftello(f); fseeko(f, li->additionalpos, SEEK_SET); // this time we want to generate XML doadditional(f, h, 2, li->additionallen); fseeko(f, savepos, SEEK_SET); // restore file position } if(xml) fputs("\t\n\n", xml); } VERBOSE("## end of layer image data @ %ld\n", (long)ftello(f)); } /** * Check PSD header; if everything seems ok, create list and xml output * files if requested, and process the layer & mask information section * to collect data on layers. (During which, description text will be sent to * the list and XML files, if they were created.) * * These output files are left open, because caller may later choose to * process image data, resulting in further output (to XML). */ int dopsd(psd_file_t f, char *psdpath, struct psd_header *h){ int result = 0; // file header fread(h->sig, 1, 4, f); h->version = get2Bu(f); get4B(f); get2B(f); // reserved[6]; h->channels = get2Bu(f); h->rows = get4B(f); h->cols = get4B(f); h->depth = get2Bu(f); h->mode = get2Bu(f); if(!feof(f) && KEYMATCH(h->sig, "8BPS")){ if(h->version == 1 #ifdef PSBSUPPORT || h->version == 2 #endif ){ openfiles(psdpath, h); if(listfile) fprintf(listfile, "-- PSD file: %s\n", psdpath); if(xml){ fputs("version, h->channels, h->rows, h->cols, h->depth, h->mode); if(h->mode >= 0 && h->mode < 16) fprintf(xml, " MODENAME='%s'", mode_names[h->mode]); fputs(">\n", xml); } UNQUIET(" PS%c (version %d), %d channels, %ld rows x %ld cols, %d bit %s\n", h->version == 1 ? 'D' : 'B', h->version, h->channels, h->rows, h->cols, h->depth, h->mode >= 0 && h->mode < 16 ? mode_names[h->mode] : "???"); if(h->channels <= 0 || h->channels > 64 || h->rows <= 0 || h->cols <= 0 || h->depth < 0 || h->depth > 32 || h->mode < 0) alwayswarn("### something isn't right about that header, giving up now.\n"); else{ h->colormodepos = ftello(f); skipblock(f, "color mode data"); if(rsrc) doimageresources(f); else skipblock(f, "image resources"); dolayermaskinfo(f, h); h->layerdatapos = ftello(f); VERBOSE("## layer data begins @ " LL_L("%lld","%ld") "\n", h->layerdatapos); result = 1; } }else alwayswarn("# \"%s\": version %d not supported\n", psdpath, h->version); }else alwayswarn("# \"%s\": couldn't read header, or is not a PSD/PSB\n", psdpath); if(!result) alwayswarn("# Try --scavenge (and related options) to see if any layer data can be found.\n"); return result; }