// Write the contents of the current directory // to the specified file. This routine doesn't // handle overwriting a directory with auxiliary // storage that's been changed. static bool TIFFWriteDirectory(TIFF tif, bool done) { if(tif.tif_mode==O.RDONLY) return true; // Clear write state so that subsequent images with // different characteristics get the right buffers // setup for them. if(done) { if((tif.tif_flags&TIF_FLAGS.TIFF_POSTENCODE)!=0) { tif.tif_flags&=~TIF_FLAGS.TIFF_POSTENCODE; if(!tif.tif_postencode(tif)) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Error post-encoding before directory write"); return false; } } tif.tif_close(tif); // shutdown encoder // Flush any data that might have been written // by the compression close+cleanup routines. if(tif.tif_rawcc>0&&(tif.tif_flags&TIF_FLAGS.TIFF_BEENWRITING)!=0&&!TIFFFlushData1(tif)) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Error flushing data before directory write"); return false; } tif.tif_rawdata=null; tif.tif_rawcc=0; tif.tif_rawdatasize=0; tif.tif_flags&=~(TIF_FLAGS.TIFF_BEENWRITING|TIF_FLAGS.TIFF_BUFFERSETUP); } TIFFDirectory td=tif.tif_dir; // Some Irfan view versions don't like RowsPerStrip set in tiled tifs if(isTiled(tif)) TIFFClrFieldBit(tif, FIELD.ROWSPERSTRIP); // Size the directory so that we can calculate // offsets for the data items that aren't kept // in-place in each field. uint nfields=0; uint[] fields=new uint[FIELD_SETLONGS]; for(FIELD b=0; b<=(FIELD)FIELD_LAST; b++) { if(TIFFFieldSet(tif, b)&&b!=FIELD.CUSTOM) nfields+=(b<FIELD.SUBFILETYPE?2u:1u); } nfields+=(uint)td.td_customValueCount; int dirsize=(int)nfields*12; // 12: sizeof(TIFFDirEntry); List<TIFFDirEntry> dirs=null; try { dirs=new List<TIFFDirEntry>(); for(int i=0; i<nfields; i++) dirs.Add(new TIFFDirEntry()); } catch { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Cannot write directory, out of space"); return false; } // Directory hasn't been placed yet, put // it at the end of the file and link it // into the existing directory structure. if(tif.tif_diroff==0&&!TIFFLinkDirectory(tif)) return false; //tif.tif_dataoff=(uint)(tif.tif_diroff+sizeof(uint16)+dirsize+sizeof(toff_t)); tif.tif_dataoff=(uint)(tif.tif_diroff+2+dirsize+4); if((tif.tif_dataoff&1)==1) tif.tif_dataoff++; TIFFSeekFile(tif, tif.tif_dataoff, SEEK.SET); tif.tif_curdir++; // Setup external form of directory // entries and write data items. td.td_fieldsset.CopyTo(fields, 0); // Write out ExtraSamples tag only if // extra samples are present in the data. if(FieldSet(fields, FIELD.EXTRASAMPLES)&&td.td_extrasamples==0) { ResetFieldBit(fields, FIELD.EXTRASAMPLES); nfields--; dirsize-=12; // 12: sizeof(TIFFDirEntry); } // XXX int dirsnumber=0; TIFFTAG tag; foreach(TIFFFieldInfo fip in tif.tif_fieldinfo) { TIFFDirEntry dir=(dirsnumber<dirs.Count)?dir=dirs[dirsnumber]:null; // For custom fields, we test to see if the custom field // is set or not. For normal fields, we just use the // FieldSet test. if(fip.field_bit==FIELD.CUSTOM) { bool is_set=false; for(int ci=0; ci<td.td_customValueCount; ci++) is_set|=(td.td_customValues[ci].info==fip); if(!is_set) continue; } else if(!FieldSet(fields, fip.field_bit)) continue; // Handle other fields. switch(fip.field_bit) { case FIELD.STRIPOFFSETS: // We use one field bit for both strip and tile // offsets, and so must be careful in selecting // the appropriate field descriptor (so that tags // are written in sorted order). tag=isTiled(tif)?TIFFTAG.TILEOFFSETS:TIFFTAG.STRIPOFFSETS; if(tag!=fip.field_tag) continue; dir.tdir_tag=(ushort)tag; dir.tdir_type=(ushort)TIFFDataType.TIFF_LONG; dir.tdir_count=td.td_nstrips; if(!TIFFWriteLongArray(tif, dir, td.td_stripoffset)) return false; break; case FIELD.STRIPBYTECOUNTS: // We use one field bit for both strip and tile // byte counts, and so must be careful in selecting // the appropriate field descriptor (so that tags // are written in sorted order). tag=isTiled(tif)?TIFFTAG.TILEBYTECOUNTS:TIFFTAG.STRIPBYTECOUNTS; if(tag!=fip.field_tag) continue; dir.tdir_tag=(ushort)tag; dir.tdir_type=(ushort)TIFFDataType.TIFF_LONG; dir.tdir_count=td.td_nstrips; if(!TIFFWriteLongArray(tif, dir, td.td_stripbytecount)) return false; break; case FIELD.ROWSPERSTRIP: TIFFSetupShortLong(tif, TIFFTAG.ROWSPERSTRIP, dir, td.td_rowsperstrip); break; case FIELD.COLORMAP: if(!TIFFWriteShortTable(tif, TIFFTAG.COLORMAP, dir, 3, td.td_colormap)) return false; break; case FIELD.IMAGEDIMENSIONS: TIFFSetupShortLong(tif, TIFFTAG.IMAGEWIDTH, dir, td.td_imagewidth); dirsnumber++; dir=dirs[dirsnumber]; TIFFSetupShortLong(tif, TIFFTAG.IMAGELENGTH, dir, td.td_imagelength); break; case FIELD.TILEDIMENSIONS: TIFFSetupShortLong(tif, TIFFTAG.TILEWIDTH, dir, td.td_tilewidth); dirsnumber++; dir=dirs[dirsnumber]; TIFFSetupShortLong(tif, TIFFTAG.TILELENGTH, dir, td.td_tilelength); break; case FIELD.COMPRESSION: TIFFSetupShort(tif, TIFFTAG.COMPRESSION, dir, (ushort)td.td_compression); break; case FIELD.PHOTOMETRIC: TIFFSetupShort(tif, TIFFTAG.PHOTOMETRIC, dir, (ushort)td.td_photometric); break; case FIELD.POSITION: if(!TIFFWriteRational(tif, TIFFDataType.TIFF_RATIONAL, TIFFTAG.XPOSITION, dir, td.td_xposition)) return false; dirsnumber++; dir=dirs[dirsnumber]; if(!TIFFWriteRational(tif, TIFFDataType.TIFF_RATIONAL, TIFFTAG.YPOSITION, dir, td.td_yposition)) return false; break; case FIELD.RESOLUTION: if(!TIFFWriteRational(tif, TIFFDataType.TIFF_RATIONAL, TIFFTAG.XRESOLUTION, dir, td.td_xresolution)) return false; dirsnumber++; dir=dirs[dirsnumber]; if(!TIFFWriteRational(tif, TIFFDataType.TIFF_RATIONAL, TIFFTAG.YRESOLUTION, dir, td.td_yresolution)) return false; break; case FIELD.BITSPERSAMPLE: case FIELD.MINSAMPLEVALUE: case FIELD.MAXSAMPLEVALUE: case FIELD.SAMPLEFORMAT: if(!TIFFWritePerSampleShorts(tif, fip.field_tag, dir)) return false; break; case FIELD.SMINSAMPLEVALUE: case FIELD.SMAXSAMPLEVALUE: if(!TIFFWritePerSampleAnys(tif, TIFFSampleToTagType(tif), fip.field_tag, dir)) return false; break; case FIELD.PAGENUMBER: case FIELD.HALFTONEHINTS: case FIELD.YCBCRSUBSAMPLING: if(!TIFFSetupShortPair(tif, fip.field_tag, dir)) return false; break; case FIELD.INKNAMES: if(!TIFFWriteInkNames(tif, dir)) return false; break; case FIELD.TRANSFERFUNCTION: if(!TIFFWriteTransferFunction(tif, dir)) return false; break; case FIELD.SUBIFD: // XXX: Always write this field using LONG type // for backward compatibility. dir.tdir_tag=(ushort)fip.field_tag; dir.tdir_type=(ushort)TIFFDataType.TIFF_LONG; dir.tdir_count=(uint)td.td_nsubifd; if(!TIFFWriteLongArray(tif, dir, td.td_subifd)) return false; // Total hack: if this directory includes a SubIFD // tag then force the next <n> directories to be // written as "sub directories" of this one. This // is used to write things like thumbnails and // image masks that one wants to keep out of the // normal directory linkage access mechanism. if(dir.tdir_count>0) { tif.tif_flags|=TIF_FLAGS.TIFF_INSUBIFD; tif.tif_nsubifd=(ushort)dir.tdir_count; if(dir.tdir_count>1) tif.tif_subifdoff=dir.tdir_offset; //was else tif.tif_subifdoff=(uint)(tif.tif_diroff+2+((char*)&dir.tdir_offset-data)); else tif.tif_subifdoff=(uint)(tif.tif_diroff+2+12*dirsnumber-4); } break; default: // XXX: Should be fixed and removed. if(fip.field_tag==TIFFTAG.DOTRANGE) { if(!TIFFSetupShortPair(tif, fip.field_tag, dir)) return false; } else if(!TIFFWriteNormalTag(tif, dir, fip)) return false; break; } dirsnumber++; if(fip.field_bit!=FIELD.CUSTOM) ResetFieldBit(fields, fip.field_bit); } // Write directory. ushort dircount=(ushort)nfields; uint diroff=tif.tif_nextdiroff; if((tif.tif_flags&TIF_FLAGS.TIFF_SWAB)!=0) { // The file's byte order is opposite to the // native machine architecture. We overwrite // the directory information with impunity // because it'll be released below after we // write it to the file. Note that all the // other tag construction routines assume that // we do this byte-swapping; i.e. they only // byte-swap indirect data. foreach(TIFFDirEntry dir in dirs) { TIFFSwab(ref dir.tdir_tag); TIFFSwab(ref dir.tdir_type); TIFFSwab(ref dir.tdir_count); TIFFSwab(ref dir.tdir_offset); } dircount=(ushort)nfields; TIFFSwab(ref dircount); TIFFSwab(ref diroff); } TIFFSeekFile(tif, tif.tif_diroff, SEEK.SET); if(!WriteOK(tif, dircount)) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Error writing directory count"); return false; } if(!WriteOK(tif, dirs, (ushort)dirsize)) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Error writing directory contents"); return false; } if(!WriteOK(tif, diroff)) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Error writing directory link"); return false; } if(done) { TIFFFreeDirectory(tif); tif.tif_flags&=~TIF_FLAGS.TIFF_DIRTYDIRECT; tif.tif_cleanup(tif); // Reset directory-related state for subsequent directories. TIFFCreateDirectory(tif); } return true; }