private static TiffFieldInfo createAnonFieldInfo(TiffTag tag, TiffType field_type) { TiffFieldInfo fld = new TiffFieldInfo(tag, TiffFieldInfo.Variable2, TiffFieldInfo.Variable2, field_type, FieldBit.Custom, true, true, null); // note that this name is a special sign to Close() and // setupFieldInfo() to free the field fld.Name = string.Format(CultureInfo.InvariantCulture, "Tag {0}", tag); return(fld); }
internal static TiffFieldInfo[] Realloc(TiffFieldInfo[] buffer, int elementCount, int newElementCount) { TiffFieldInfo[] newBuffer = new TiffFieldInfo [newElementCount]; if (buffer != null) { int copyLength = Math.Min(elementCount, newElementCount); Array.Copy(buffer, newBuffer, copyLength); } return(newBuffer); }
private int fetchFailed(TiffDirEntry dir) { string format = "Error fetching data for field \"{0}\""; TiffFieldInfo fi = FieldWithTag(dir.tdir_tag); if (fi.Bit == FieldBit.Custom) { // According to the TIFF standard, private (custom) tags can be safely // ignored when reading an image from a TIFF file. WarningExt(this, m_clientdata, m_name, format, fi.Name); } else { ErrorExt(this, m_clientdata, m_name, format, fi.Name); } return(0); }
/// <summary> /// Writes tags that are not special cased. /// </summary> private bool writeNormalTag(ref TiffDirEntry dir, TiffFieldInfo fip) { short wc = fip.WriteCount; dir.tdir_tag = fip.Tag; dir.tdir_type = fip.Type; dir.tdir_count = wc; switch (fip.Type) { case TiffType.SHORT: case TiffType.SSHORT: if (fip.PassCount) { short[] wp; int wc2; if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); wp = result[1].ToShortArray(); dir.tdir_count = wc2; } else { // Assume TiffFieldInfo.Variable FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); wp = result[1].ToShortArray(); dir.tdir_count = wc; } if (!writeShortArray(ref dir, wp)) return false; } else { if (wc == 1) { FieldValue[] result = GetField(fip.Tag); short sv = result[0].ToShort(); dir.tdir_offset = insertData(dir.tdir_type, sv); } else { FieldValue[] result = GetField(fip.Tag); short[] wp = result[0].ToShortArray(); if (!writeShortArray(ref dir, wp)) return false; } } break; case TiffType.LONG: case TiffType.SLONG: case TiffType.IFD: if (fip.PassCount) { int[] lp; int wc2; if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); lp = result[1].ToIntArray(); dir.tdir_count = wc2; } else { // Assume TiffFieldInfo.Variable FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); lp = result[1].ToIntArray(); dir.tdir_count = wc; } if (!writeLongArray(ref dir, lp)) return false; } else { if (wc == 1) { // XXX handle LONG->SHORT conversion FieldValue[] result = GetField(fip.Tag); dir.tdir_offset = result[0].ToUInt(); } else { int[] lp; FieldValue[] result = GetField(fip.Tag); lp = result[0].ToIntArray(); if (!writeLongArray(ref dir, lp)) return false; } } break; case TiffType.RATIONAL: case TiffType.SRATIONAL: if (fip.PassCount) { float[] fp; int wc2; if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); fp = result[1].ToFloatArray(); dir.tdir_count = wc2; } else { // Assume TiffFieldInfo.Variable FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); fp = result[1].ToFloatArray(); dir.tdir_count = wc; } if (!writeRationalArray(ref dir, fp)) return false; } else { if (wc == 1) { float[] fv = new float[1]; FieldValue[] result = GetField(fip.Tag); fv[0] = result[0].ToFloat(); if (!writeRationalArray(ref dir, fv)) return false; } else { FieldValue[] result = GetField(fip.Tag); float[] fp = result[0].ToFloatArray(); if (!writeRationalArray(ref dir, fp)) return false; } } break; case TiffType.FLOAT: if (fip.PassCount) { float[] fp; int wc2; if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); fp = result[1].ToFloatArray(); dir.tdir_count = wc2; } else { // Assume TiffFieldInfo.Variable FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); fp = result[1].ToFloatArray(); dir.tdir_count = wc; } if (!writeFloatArray(ref dir, fp)) return false; } else { if (wc == 1) { float[] fv = new float[1]; FieldValue[] result = GetField(fip.Tag); fv[0] = result[0].ToFloat(); if (!writeFloatArray(ref dir, fv)) return false; } else { FieldValue[] result = GetField(fip.Tag); float[] fp = result[0].ToFloatArray(); if (!writeFloatArray(ref dir, fp)) return false; } } break; case TiffType.DOUBLE: if (fip.PassCount) { double[] dp; int wc2; if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); dp = result[1].ToDoubleArray(); dir.tdir_count = wc2; } else { // Assume TiffFieldInfo.Variable FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); dp = result[1].ToDoubleArray(); dir.tdir_count = wc; } if (!writeDoubleArray(ref dir, dp)) return false; } else { if (wc == 1) { double[] dv = new double[1]; FieldValue[] result = GetField(fip.Tag); dv[0] = result[0].ToDouble(); if (!writeDoubleArray(ref dir, dv)) return false; } else { FieldValue[] result = GetField(fip.Tag); double[] dp = result[0].ToDoubleArray(); if (!writeDoubleArray(ref dir, dp)) return false; } } break; case TiffType.ASCII: { FieldValue[] result = GetField(fip.Tag); string cp; if (fip.PassCount) cp = result[1].ToString(); else cp = result[0].ToString(); // add zero ('\0') at the end of the byte array byte[] stringBytes = Latin1Encoding.GetBytes(cp); byte[] totalBytes = new byte[stringBytes.Length + 1]; Buffer.BlockCopy(stringBytes, 0, totalBytes, 0, stringBytes.Length); dir.tdir_count = totalBytes.Length; if (!writeByteArray(ref dir, totalBytes)) return false; } break; case TiffType.BYTE: case TiffType.SBYTE: if (fip.PassCount) { byte[] cp; int wc2; if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); cp = result[1].ToByteArray(); dir.tdir_count = wc2; } else { // Assume TiffFieldInfo.Variable FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); cp = result[1].ToByteArray(); dir.tdir_count = wc; } if (!writeByteArray(ref dir, cp)) return false; } else { if (wc == 1) { byte[] cv = new byte[1]; FieldValue[] result = GetField(fip.Tag); cv[0] = result[0].ToByte(); if (!writeByteArray(ref dir, cv)) return false; } else { FieldValue[] result = GetField(fip.Tag); byte[] cp = result[0].ToByteArray(); if (!writeByteArray(ref dir, cp)) return false; } } break; case TiffType.UNDEFINED: { byte[] cp; int wc2; if (wc == TiffFieldInfo.Variable) { FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); cp = result[1].ToByteArray(); dir.tdir_count = wc; } else if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); cp = result[1].ToByteArray(); dir.tdir_count = wc2; } else { FieldValue[] result = GetField(fip.Tag); cp = result[0].ToByteArray(); } if (!writeByteArray(ref dir, cp)) return false; } break; case TiffType.NOTYPE: break; } return true; }
/// <summary> /// Merges given field information to existing one. /// </summary> /// <param name="info">The array of <see cref="TiffFieldInfo"/> objects.</param> /// <param name="count">The number of items to use from the <paramref name="info"/> array.</param> public void MergeFieldInfo(TiffFieldInfo[] info, int count) { m_foundfield = null; if (m_nfields <= 0) m_fieldinfo = new TiffFieldInfo[count]; for (int i = 0; i < count; i++) { TiffFieldInfo fip = FindFieldInfo(info[i].Tag, info[i].Type); // only add definitions that aren't already present if (fip == null) { m_fieldinfo = Realloc(m_fieldinfo, m_nfields, m_nfields + 1); m_fieldinfo[m_nfields] = info[i]; m_nfields++; } } // Sort the field info by tag number IComparer myComparer = new TagCompare(); Array.Sort(m_fieldinfo, 0, m_nfields, myComparer); }
/// <summary> /// Reads a custom directory from the arbitrary offset within file/stream. /// </summary> /// <param name="offset">The directory offset.</param> /// <param name="info">The array of <see cref="TiffFieldInfo"/> objects to merge to /// existing field information.</param> /// <param name="count">The number of items to use from /// the <paramref name="info"/> array.</param> /// <returns><c>true</c> if a custom directory was read successfully; /// otherwise, <c>false</c></returns> public bool ReadCustomDirectory(long offset, TiffFieldInfo[] info, int count) { const string module = "ReadCustomDirectory"; MergeFieldInfo(info, count); uint dummyNextDirOff; TiffDirEntry[] dir; short dircount = fetchDirectory((uint)offset, out dir, out dummyNextDirOff); if (dircount == 0) { ErrorExt(this, m_clientdata, module, "{0}: Failed to read custom directory at offset {1}", m_name, offset); return false; } FreeDirectory(); m_dir = new TiffDirectory(); int fix = 0; for (short i = 0; i < dircount; i++) { if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) { short temp = (short)dir[i].tdir_tag; SwabShort(ref temp); dir[i].tdir_tag = (TiffTag)(ushort)temp; temp = (short)dir[i].tdir_type; SwabShort(ref temp); dir[i].tdir_type = (TiffType)temp; SwabLong(ref dir[i].tdir_count); SwabUInt(ref dir[i].tdir_offset); } if (fix >= m_nfields || dir[i].tdir_tag == TiffTag.IGNORE) continue; while (fix < m_nfields && m_fieldinfo[fix].Tag < dir[i].tdir_tag) fix++; if (fix >= m_nfields || m_fieldinfo[fix].Tag != dir[i].tdir_tag) { WarningExt(this, m_clientdata, module, "{0}: unknown field with tag {1} (0x{2:x}) encountered", m_name, (ushort)dir[i].tdir_tag, (ushort)dir[i].tdir_tag); TiffFieldInfo[] arr = new TiffFieldInfo[1]; arr[0] = createAnonFieldInfo(dir[i].tdir_tag, dir[i].tdir_type); MergeFieldInfo(arr, 1); fix = 0; while (fix < m_nfields && m_fieldinfo[fix].Tag < dir[i].tdir_tag) fix++; } // null out old tags that we ignore. if (m_fieldinfo[fix].Bit == FieldBit.Ignore) { dir[i].tdir_tag = TiffTag.IGNORE; continue; } // Check data type. TiffFieldInfo fip = m_fieldinfo[fix]; while (dir[i].tdir_type != fip.Type && fix < m_nfields) { if (fip.Type == TiffType.ANY) { // wildcard break; } fip = m_fieldinfo[++fix]; if (fix >= m_nfields || fip.Tag != dir[i].tdir_tag) { WarningExt(this, m_clientdata, module, "{0}: wrong data type {1} for \"{2}\"; tag ignored", m_name, dir[i].tdir_type, m_fieldinfo[fix - 1].Name); dir[i].tdir_tag = TiffTag.IGNORE; continue; } } // Check count if known in advance. if (fip.ReadCount != TiffFieldInfo.Variable && fip.ReadCount != TiffFieldInfo.Variable2) { int expected = fip.ReadCount; if (fip.ReadCount == TiffFieldInfo.Spp) expected = m_dir.td_samplesperpixel; if (!checkDirCount(dir[i], expected)) { dir[i].tdir_tag = TiffTag.IGNORE; continue; } } // EXIF tags which need to be specifically processed. switch (dir[i].tdir_tag) { case TiffTag.EXIF_SUBJECTDISTANCE: fetchSubjectDistance(dir[i]); break; default: fetchNormalTag(dir[i]); break; } } return true; }
/// <summary> /// Reads the contents of the next TIFF directory in an open TIFF file/stream and makes /// it the current directory. /// </summary> /// <returns><c>true</c> if directory was successfully read; otherwise, <c>false</c> if an /// error was encountered, or if there are no more directories to be read.</returns> /// <remarks><para>Directories are read sequentially.</para> /// <para>Applications only need to call <see cref="ReadDirectory"/> to read multiple /// subfiles in a single TIFF file/stream - the first directory in a file/stream is /// automatically read when <see cref="Open(string, string)"/> or /// <see cref="ClientOpen(string, string, object, BitMiracle.LibTiff.Classic.TiffStream)"/> is called. /// </para><para> /// The images that have a single uncompressed strip or tile of data are automatically /// treated as if they were made up of multiple strips or tiles of approximately 8 /// kilobytes each. This operation is done only in-memory; it does not alter the contents /// of the file/stream. However, the construction of the "chopped strips" is visible to /// the application through the number of strips returned by <see cref="NumberOfStrips"/> /// or the number of tiles returned by <see cref="NumberOfTiles"/>.</para> /// </remarks> public bool ReadDirectory() { const string module = "ReadDirectory"; m_diroff = m_nextdiroff; if (m_diroff == 0) { // no more directories return false; } // Check whether we have the last offset or bad offset (IFD looping). if (!checkDirOffset(m_nextdiroff)) return false; // Cleanup any previous compression state. m_currentCodec.Cleanup(); m_curdir++; TiffDirEntry[] dir; short dircount = fetchDirectory(m_nextdiroff, out dir, out m_nextdiroff); if (dircount == 0) { ErrorExt(this, m_clientdata, module, "{0}: Failed to read directory at offset {1}", m_name, m_nextdiroff); return false; } // reset before new dir m_flags &= ~TiffFlags.BEENWRITING; // Setup default value and then make a pass over the fields to check type and tag // information, and to extract info required to size data structures. A second pass is // made afterwards to read in everthing not taken in the first pass. // free any old stuff and reinit FreeDirectory(); setupDefaultDirectory(); // Electronic Arts writes gray-scale TIFF files without a PlanarConfiguration // directory entry. Thus we setup a default value here, even though the TIFF spec says // there is no default value. SetField(TiffTag.PLANARCONFIG, PlanarConfig.CONTIG); // Sigh, we must make a separate pass through the directory for the following reason: // // We must process the Compression tag in the first pass in order to merge in // codec-private tag definitions (otherwise we may get complaints about unknown tags). // However, the Compression tag may be dependent on the SamplesPerPixel tag value // because older TIFF specs permited Compression to be written as a // SamplesPerPixel-count tag entry. Thus if we don't first figure out the correct // SamplesPerPixel tag value then we may end up ignoring the Compression tag value // because it has an incorrect count value (if the true value of SamplesPerPixel is not 1). // // It sure would have been nice if Aldus had really thought this stuff through carefully. for (int i = 0; i < dircount; i++) { TiffDirEntry dp = dir[i]; if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) { short temp = (short)dp.tdir_tag; SwabShort(ref temp); dp.tdir_tag = (TiffTag)(ushort)temp; temp = (short)dp.tdir_type; SwabShort(ref temp); dp.tdir_type = (TiffType)temp; SwabLong(ref dp.tdir_count); SwabUInt(ref dp.tdir_offset); } if (dp.tdir_tag == TiffTag.SAMPLESPERPIXEL) { if (!fetchNormalTag(dir[i])) return false; dp.tdir_tag = TiffTag.IGNORE; } } // First real pass over the directory. int fix = 0; bool diroutoforderwarning = false; bool haveunknowntags = false; for (int i = 0; i < dircount; i++) { if (dir[i].tdir_tag == TiffTag.IGNORE) continue; if (fix >= m_nfields) fix = 0; // Silicon Beach (at least) writes unordered directory tags (violating the spec). // Handle it here, but be obnoxious (maybe they'll fix it?). if (dir[i].tdir_tag < m_fieldinfo[fix].Tag) { if (!diroutoforderwarning) { WarningExt(this, m_clientdata, module, "{0}: invalid TIFF directory; tags are not sorted in ascending order", m_name); diroutoforderwarning = true; } fix = 0; // O(n^2) } while (fix < m_nfields && m_fieldinfo[fix].Tag < dir[i].tdir_tag) fix++; if (fix >= m_nfields || m_fieldinfo[fix].Tag != dir[i].tdir_tag) { // Unknown tag ... we'll deal with it below haveunknowntags = true; continue; } // null out old tags that we ignore. if (m_fieldinfo[fix].Bit == FieldBit.Ignore) { dir[i].tdir_tag = TiffTag.IGNORE; continue; } // Check data type. TiffFieldInfo fip = m_fieldinfo[fix]; bool tagIgnored = false; while (dir[i].tdir_type != fip.Type && fix < m_nfields) { if (fip.Type == TiffType.ANY) { // wildcard break; } fip = m_fieldinfo[++fix]; if (fix >= m_nfields || fip.Tag != dir[i].tdir_tag) { WarningExt(this, m_clientdata, module, "{0}: wrong data type {1} for \"{2}\"; tag ignored", m_name, dir[i].tdir_type, m_fieldinfo[fix - 1].Name); dir[i].tdir_tag = TiffTag.IGNORE; tagIgnored = true; break; } } if (tagIgnored) continue; // Check count if known in advance. if (fip.ReadCount != TiffFieldInfo.Variable && fip.ReadCount != TiffFieldInfo.Variable2) { int expected = fip.ReadCount; if (fip.ReadCount == TiffFieldInfo.Spp) expected = m_dir.td_samplesperpixel; if (!checkDirCount(dir[i], expected)) { dir[i].tdir_tag = TiffTag.IGNORE; continue; } } switch (dir[i].tdir_tag) { case TiffTag.COMPRESSION: // The 5.0 spec says the Compression tag has one value, // while earlier specs say it has one value per sample. // Because of this, we accept the tag if one value is supplied. if (dir[i].tdir_count == 1) { int v = extractData(dir[i]); if (!SetField(dir[i].tdir_tag, v)) return false; break; // XXX: workaround for broken TIFFs } else if (dir[i].tdir_type == TiffType.LONG) { int v; if (!fetchPerSampleLongs(dir[i], out v) || !SetField(dir[i].tdir_tag, v)) return false; } else { short iv; if (!fetchPerSampleShorts(dir[i], out iv) || !SetField(dir[i].tdir_tag, iv)) return false; } dir[i].tdir_tag = TiffTag.IGNORE; break; case TiffTag.STRIPOFFSETS: case TiffTag.STRIPBYTECOUNTS: case TiffTag.TILEOFFSETS: case TiffTag.TILEBYTECOUNTS: setFieldBit(fip.Bit); break; case TiffTag.IMAGEWIDTH: case TiffTag.IMAGELENGTH: case TiffTag.IMAGEDEPTH: case TiffTag.TILELENGTH: case TiffTag.TILEWIDTH: case TiffTag.TILEDEPTH: case TiffTag.PLANARCONFIG: case TiffTag.ROWSPERSTRIP: case TiffTag.EXTRASAMPLES: if (!fetchNormalTag(dir[i])) return false; dir[i].tdir_tag = TiffTag.IGNORE; break; } } // If we saw any unknown tags, make an extra pass over the directory to deal with // them. This must be done separately because the tags could have become known when we // registered a codec after finding the Compression tag. In a correctly-sorted // directory there's no problem because Compression will come before any codec-private // tags, but if the sorting is wrong that might not hold. if (haveunknowntags) { fix = 0; for (int i = 0; i < dircount; i++) { if (dir[i].tdir_tag == TiffTag.IGNORE) continue; if (fix >= m_nfields || dir[i].tdir_tag < m_fieldinfo[fix].Tag) { // O(n^2) fix = 0; } while (fix < m_nfields && m_fieldinfo[fix].Tag < dir[i].tdir_tag) fix++; if (fix >= m_nfields || m_fieldinfo[fix].Tag != dir[i].tdir_tag) { Tiff.WarningExt(this, m_clientdata, module, "{0}: unknown field with tag {1} (0x{2:x}) encountered", m_name, (ushort)dir[i].tdir_tag, (ushort)dir[i].tdir_tag); TiffFieldInfo[] arr = new TiffFieldInfo[1]; arr[0] = createAnonFieldInfo(dir[i].tdir_tag, dir[i].tdir_type); MergeFieldInfo(arr, 1); fix = 0; while (fix < m_nfields && m_fieldinfo[fix].Tag < dir[i].tdir_tag) fix++; } // Check data type. TiffFieldInfo fip = m_fieldinfo[fix]; while (dir[i].tdir_type != fip.Type && fix < m_nfields) { if (fip.Type == TiffType.ANY) { // wildcard break; } fip = m_fieldinfo[++fix]; if (fix >= m_nfields || fip.Tag != dir[i].tdir_tag) { Tiff.WarningExt(this, m_clientdata, module, "{0}: wrong data type {1} for \"{2}\"; tag ignored", m_name, dir[i].tdir_type, m_fieldinfo[fix - 1].Name); dir[i].tdir_tag = TiffTag.IGNORE; break; } } } } // XXX: OJPEG hack. // If a) compression is OJPEG, b) planarconfig tag says it's separate, c) strip // offsets/bytecounts tag are both present and d) both contain exactly one value, then // we consistently find that the buggy implementation of the buggy compression scheme // matches contig planarconfig best. So we 'fix-up' the tag here if ((m_dir.td_compression == Compression.OJPEG) && (m_dir.td_planarconfig == PlanarConfig.SEPARATE)) { int dpIndex = readDirectoryFind(dir, dircount, TiffTag.STRIPOFFSETS); if (dpIndex != -1 && dir[dpIndex].tdir_count == 1) { dpIndex = readDirectoryFind(dir, dircount, TiffTag.STRIPBYTECOUNTS); if (dpIndex != -1 && dir[dpIndex].tdir_count == 1) { m_dir.td_planarconfig = PlanarConfig.CONTIG; WarningExt(this, m_clientdata, "ReadDirectory", "Planarconfig tag value assumed incorrect, assuming data is contig instead of chunky"); } } } // Allocate directory structure and setup defaults. if (!fieldSet(FieldBit.ImageDimensions)) { missingRequired("ImageLength"); return false; } // Setup appropriate structures (by strip or by tile) if (!fieldSet(FieldBit.TileDimensions)) { m_dir.td_nstrips = NumberOfStrips(); m_dir.td_tilewidth = m_dir.td_imagewidth; m_dir.td_tilelength = m_dir.td_rowsperstrip; m_dir.td_tiledepth = m_dir.td_imagedepth; m_flags &= ~TiffFlags.ISTILED; } else { m_dir.td_nstrips = NumberOfTiles(); m_flags |= TiffFlags.ISTILED; } if (m_dir.td_nstrips == 0) { ErrorExt(this, m_clientdata, module, "{0}: cannot handle zero number of {1}", m_name, IsTiled() ? "tiles" : "strips"); return false; } m_dir.td_stripsperimage = m_dir.td_nstrips; if (m_dir.td_planarconfig == PlanarConfig.SEPARATE) m_dir.td_stripsperimage /= m_dir.td_samplesperpixel; if (!fieldSet(FieldBit.StripOffsets)) { if ((m_dir.td_compression == Compression.OJPEG) && !IsTiled() && (m_dir.td_nstrips == 1)) { // XXX: OJPEG hack. // If a) compression is OJPEG, b) it's not a tiled TIFF, and c) the number of // strips is 1, then we tolerate the absence of stripoffsets tag, because, // presumably, all required data is in the JpegInterchangeFormat stream. setFieldBit(FieldBit.StripOffsets); } else { missingRequired(IsTiled() ? "TileOffsets" : "StripOffsets"); return false; } } // Second pass: extract other information. for (int i = 0; i < dircount; i++) { if (dir[i].tdir_tag == TiffTag.IGNORE) continue; switch (dir[i].tdir_tag) { case TiffTag.MINSAMPLEVALUE: case TiffTag.MAXSAMPLEVALUE: case TiffTag.BITSPERSAMPLE: case TiffTag.DATATYPE: case TiffTag.SAMPLEFORMAT: // The 5.0 spec says the Compression tag has one value, while earlier // specs say it has one value per sample. Because of this, we accept the // tag if one value is supplied. // // The MinSampleValue, MaxSampleValue, BitsPerSample DataType and // SampleFormat tags are supposed to be written as one value/sample, but // some vendors incorrectly write one value only - so we accept that as // well (yech). Other vendors write correct value for NumberOfSamples, but // incorrect one for BitsPerSample and friends, and we will read this too. if (dir[i].tdir_count == 1) { int v = extractData(dir[i]); if (!SetField(dir[i].tdir_tag, v)) return false; // XXX: workaround for broken TIFFs } else if (dir[i].tdir_tag == TiffTag.BITSPERSAMPLE && dir[i].tdir_type == TiffType.LONG) { int v; if (!fetchPerSampleLongs(dir[i], out v) || !SetField(dir[i].tdir_tag, v)) return false; } else { short iv; if (!fetchPerSampleShorts(dir[i], out iv) || !SetField(dir[i].tdir_tag, iv)) return false; } break; case TiffTag.SMINSAMPLEVALUE: case TiffTag.SMAXSAMPLEVALUE: double dv; if (!fetchPerSampleAnys(dir[i], out dv) || !SetField(dir[i].tdir_tag, dv)) return false; break; case TiffTag.STRIPOFFSETS: case TiffTag.TILEOFFSETS: if (!fetchStripThing(dir[i], m_dir.td_nstrips, ref m_dir.td_stripoffset)) return false; break; case TiffTag.STRIPBYTECOUNTS: case TiffTag.TILEBYTECOUNTS: if (!fetchStripThing(dir[i], m_dir.td_nstrips, ref m_dir.td_stripbytecount)) return false; break; case TiffTag.COLORMAP: case TiffTag.TRANSFERFUNCTION: { // TransferFunction can have either 1x or 3x data values; // Colormap can have only 3x items. int v = 1 << m_dir.td_bitspersample; if (dir[i].tdir_tag == TiffTag.COLORMAP || dir[i].tdir_count != v) { if (!checkDirCount(dir[i], 3 * v)) break; } byte[] cp = new byte[dir[i].tdir_count * sizeof(short)]; if (fetchData(dir[i], cp) != 0) { int c = 1 << m_dir.td_bitspersample; if (dir[i].tdir_count == c) { // This deals with there being only one array to apply to all samples. short[] u = ByteArrayToShorts(cp, 0, dir[i].tdir_count * sizeof(short)); SetField(dir[i].tdir_tag, u, u, u); } else { v *= sizeof(short); short[] u0 = ByteArrayToShorts(cp, 0, v); short[] u1 = ByteArrayToShorts(cp, v, v); short[] u2 = ByteArrayToShorts(cp, 2 * v, v); SetField(dir[i].tdir_tag, u0, u1, u2); } } break; } case TiffTag.PAGENUMBER: case TiffTag.HALFTONEHINTS: case TiffTag.YCBCRSUBSAMPLING: case TiffTag.DOTRANGE: fetchShortPair(dir[i]); break; case TiffTag.REFERENCEBLACKWHITE: fetchRefBlackWhite(dir[i]); break; // BEGIN REV 4.0 COMPATIBILITY case TiffTag.OSUBFILETYPE: FileType ft = 0; switch ((OFileType)extractData(dir[i])) { case OFileType.REDUCEDIMAGE: ft = FileType.REDUCEDIMAGE; break; case OFileType.PAGE: ft = FileType.PAGE; break; } if (ft != 0) SetField(TiffTag.SUBFILETYPE, ft); break; // END REV 4.0 COMPATIBILITY default: fetchNormalTag(dir[i]); break; } } // OJPEG hack: // - If a) compression is OJPEG, and b) photometric tag is missing, then we // consistently find that photometric should be YCbCr // - If a) compression is OJPEG, and b) photometric tag says it's RGB, then we // consistently find that the buggy implementation of the buggy compression scheme // matches photometric YCbCr instead. // - If a) compression is OJPEG, and b) bitspersample tag is missing, then we // consistently find bitspersample should be 8. // - If a) compression is OJPEG, b) samplesperpixel tag is missing, and c) photometric // is RGB or YCbCr, then we consistently find samplesperpixel should be 3 // - If a) compression is OJPEG, b) samplesperpixel tag is missing, and c) photometric // is MINISWHITE or MINISBLACK, then we consistently find samplesperpixel should be 3 if (m_dir.td_compression == Compression.OJPEG) { if (!fieldSet(FieldBit.Photometric)) { WarningExt(this, m_clientdata, "ReadDirectory", "Photometric tag is missing, assuming data is YCbCr"); if (!SetField(TiffTag.PHOTOMETRIC, Photometric.YCBCR)) return false; } else if (m_dir.td_photometric == Photometric.RGB) { m_dir.td_photometric = Photometric.YCBCR; WarningExt(this, m_clientdata, "ReadDirectory", "Photometric tag value assumed incorrect, assuming data is YCbCr instead of RGB"); } if (!fieldSet(FieldBit.BitsPerSample)) { WarningExt(this, m_clientdata, "ReadDirectory", "BitsPerSample tag is missing, assuming 8 bits per sample"); if (!SetField(TiffTag.BITSPERSAMPLE, 8)) return false; } if (!fieldSet(FieldBit.SamplesPerPixel)) { if ((m_dir.td_photometric == Photometric.RGB) || (m_dir.td_photometric == Photometric.YCBCR)) { WarningExt(this, m_clientdata, "ReadDirectory", "SamplesPerPixel tag is missing, assuming correct SamplesPerPixel value is 3"); if (!SetField(TiffTag.SAMPLESPERPIXEL, 3)) return false; } else if ((m_dir.td_photometric == Photometric.MINISWHITE) || (m_dir.td_photometric == Photometric.MINISBLACK)) { WarningExt(this, m_clientdata, "ReadDirectory", "SamplesPerPixel tag is missing, assuming correct SamplesPerPixel value is 1"); if (!SetField(TiffTag.SAMPLESPERPIXEL, 1)) return false; } } } // Verify Palette image has a Colormap. if (m_dir.td_photometric == Photometric.PALETTE && !fieldSet(FieldBit.ColorMap)) { missingRequired("Colormap"); return false; } // OJPEG hack: // We do no further messing with strip/tile offsets/bytecounts in OJPEG TIFFs if (m_dir.td_compression != Compression.OJPEG) { // Attempt to deal with a missing StripByteCounts tag. if (!fieldSet(FieldBit.StripByteCounts)) { // Some manufacturers violate the spec by not giving the size of the strips. // In this case, assume there is one uncompressed strip of data. if ((m_dir.td_planarconfig == PlanarConfig.CONTIG && m_dir.td_nstrips > 1) || (m_dir.td_planarconfig == PlanarConfig.SEPARATE && m_dir.td_nstrips != m_dir.td_samplesperpixel)) { missingRequired("StripByteCounts"); return false; } WarningExt(this, m_clientdata, module, "{0}: TIFF directory is missing required \"{1}\" field, calculating from imagelength", m_name, FieldWithTag(TiffTag.STRIPBYTECOUNTS).Name); if (!estimateStripByteCounts(dir, dircount)) return false; } else if (m_dir.td_nstrips == 1 && m_dir.td_stripoffset[0] != 0 && byteCountLooksBad(m_dir)) { // XXX: Plexus (and others) sometimes give a value of zero for a tag when // they don't know what the correct value is! Try and handle the simple case // of estimating the size of a one strip image. WarningExt(this, m_clientdata, module, "{0}: Bogus \"{1}\" field, ignoring and calculating from imagelength", m_name, FieldWithTag(TiffTag.STRIPBYTECOUNTS).Name); if (!estimateStripByteCounts(dir, dircount)) return false; } else if (m_dir.td_planarconfig == PlanarConfig.CONTIG && m_dir.td_nstrips > 2 && m_dir.td_compression == Compression.NONE && m_dir.td_stripbytecount[0] != m_dir.td_stripbytecount[1]) { // XXX: Some vendors fill StripByteCount array with absolutely wrong values // (it can be equal to StripOffset array, for example). Catch this case here. WarningExt(this, m_clientdata, module, "{0}: Wrong \"{1}\" field, ignoring and calculating from imagelength", m_name, FieldWithTag(TiffTag.STRIPBYTECOUNTS).Name); if (!estimateStripByteCounts(dir, dircount)) return false; } } dir = null; if (!fieldSet(FieldBit.MaxSampleValue)) m_dir.td_maxsamplevalue = (ushort)((1 << m_dir.td_bitspersample) - 1); // Setup default compression scheme. // XXX: We can optimize checking for the strip bounds using the sorted bytecounts // array. See also comments for appendToStrip() function. if (m_dir.td_nstrips > 1) { m_dir.td_stripbytecountsorted = true; for (int strip = 1; strip < m_dir.td_nstrips; strip++) { if (m_dir.td_stripoffset[strip - 1] > m_dir.td_stripoffset[strip]) { m_dir.td_stripbytecountsorted = false; break; } } } if (!fieldSet(FieldBit.Compression)) SetField(TiffTag.COMPRESSION, Compression.NONE); // Some manufacturers make life difficult by writing large amounts of uncompressed // data as a single strip. This is contrary to the recommendations of the spec. The // following makes an attempt at breaking such images into strips closer to the // recommended 8k bytes. A side effect, however, is that the RowsPerStrip tag value // may be changed. if (m_dir.td_nstrips == 1 && m_dir.td_compression == Compression.NONE && (m_flags & TiffFlags.STRIPCHOP) == TiffFlags.STRIPCHOP && (m_flags & TiffFlags.ISTILED) != TiffFlags.ISTILED) { chopUpSingleUncompressedStrip(); } // Reinitialize i/o since we are starting on a new directory. m_row = -1; m_curstrip = -1; m_col = -1; m_curtile = -1; m_tilesize = -1; m_scanlinesize = ScanlineSize(); if (m_scanlinesize == 0) { ErrorExt(this, m_clientdata, module, "{0}: cannot handle zero scanline size", m_name); return false; } if (IsTiled()) { m_tilesize = TileSize(); if (m_tilesize == 0) { ErrorExt(this, m_clientdata, module, "{0}: cannot handle zero tile size", m_name); return false; } } else { if (StripSize() == 0) { ErrorExt(this, m_clientdata, module, "{0}: cannot handle zero strip size", m_name); return false; } } return true; }
/// <summary> /// Gets the value(s) of a tag in an open TIFF file. /// </summary> /// <param name="tif">An instance of the <see cref="Tiff"/> class.</param> /// <param name="tag">The tag.</param> /// <returns>The value(s) of a tag in an open TIFF file/stream as array of /// <see cref="FieldValue"/> objects or <c>null</c> if there is no such tag set.</returns> /// <seealso cref="Tiff.GetField"/> public virtual FieldValue[] GetField(Tiff tif, TiffTag tag) { TiffDirectory td = tif.m_dir; FieldValue[] result = null; switch (tag) { case TiffTag.SUBFILETYPE: result = new FieldValue[1]; result[0].Set(td.td_subfiletype); break; case TiffTag.IMAGEWIDTH: result = new FieldValue[1]; result[0].Set(td.td_imagewidth); break; case TiffTag.IMAGELENGTH: result = new FieldValue[1]; result[0].Set(td.td_imagelength); break; case TiffTag.BITSPERSAMPLE: result = new FieldValue[1]; result[0].Set(td.td_bitspersample); break; case TiffTag.COMPRESSION: result = new FieldValue[1]; result[0].Set(td.td_compression); break; case TiffTag.PHOTOMETRIC: result = new FieldValue[1]; result[0].Set(td.td_photometric); break; case TiffTag.THRESHHOLDING: result = new FieldValue[1]; result[0].Set(td.td_threshholding); break; case TiffTag.FILLORDER: result = new FieldValue[1]; result[0].Set(td.td_fillorder); break; case TiffTag.ORIENTATION: result = new FieldValue[1]; result[0].Set(td.td_orientation); break; case TiffTag.SAMPLESPERPIXEL: result = new FieldValue[1]; result[0].Set(td.td_samplesperpixel); break; case TiffTag.ROWSPERSTRIP: result = new FieldValue[1]; result[0].Set(td.td_rowsperstrip); break; case TiffTag.MINSAMPLEVALUE: result = new FieldValue[1]; result[0].Set(td.td_minsamplevalue); break; case TiffTag.MAXSAMPLEVALUE: result = new FieldValue[1]; result[0].Set(td.td_maxsamplevalue); break; case TiffTag.SMINSAMPLEVALUE: result = new FieldValue[1]; result[0].Set(td.td_sminsamplevalue); break; case TiffTag.SMAXSAMPLEVALUE: result = new FieldValue[1]; result[0].Set(td.td_smaxsamplevalue); break; case TiffTag.XRESOLUTION: result = new FieldValue[1]; result[0].Set(td.td_xresolution); break; case TiffTag.YRESOLUTION: result = new FieldValue[1]; result[0].Set(td.td_yresolution); break; case TiffTag.PLANARCONFIG: result = new FieldValue[1]; result[0].Set(td.td_planarconfig); break; case TiffTag.XPOSITION: result = new FieldValue[1]; result[0].Set(td.td_xposition); break; case TiffTag.YPOSITION: result = new FieldValue[1]; result[0].Set(td.td_yposition); break; case TiffTag.RESOLUTIONUNIT: result = new FieldValue[1]; result[0].Set(td.td_resolutionunit); break; case TiffTag.PAGENUMBER: result = new FieldValue[2]; result[0].Set(td.td_pagenumber[0]); result[1].Set(td.td_pagenumber[1]); break; case TiffTag.HALFTONEHINTS: result = new FieldValue[2]; result[0].Set(td.td_halftonehints[0]); result[1].Set(td.td_halftonehints[1]); break; case TiffTag.COLORMAP: result = new FieldValue[3]; result[0].Set(td.td_colormap[0]); result[1].Set(td.td_colormap[1]); result[2].Set(td.td_colormap[2]); break; case TiffTag.STRIPOFFSETS: case TiffTag.TILEOFFSETS: result = new FieldValue[1]; result[0].Set(td.td_stripoffset); break; case TiffTag.STRIPBYTECOUNTS: case TiffTag.TILEBYTECOUNTS: result = new FieldValue[1]; result[0].Set(td.td_stripbytecount); break; case TiffTag.MATTEING: result = new FieldValue[1]; result[0].Set((td.td_extrasamples == 1 && td.td_sampleinfo[0] == ExtraSample.ASSOCALPHA)); break; case TiffTag.EXTRASAMPLES: result = new FieldValue[2]; result[0].Set(td.td_extrasamples); result[1].Set(td.td_sampleinfo); break; case TiffTag.TILEWIDTH: result = new FieldValue[1]; result[0].Set(td.td_tilewidth); break; case TiffTag.TILELENGTH: result = new FieldValue[1]; result[0].Set(td.td_tilelength); break; case TiffTag.TILEDEPTH: result = new FieldValue[1]; result[0].Set(td.td_tiledepth); break; case TiffTag.DATATYPE: switch (td.td_sampleformat) { case SampleFormat.UINT: result = new FieldValue[1]; result[0].Set(DATATYPE_UINT); break; case SampleFormat.INT: result = new FieldValue[1]; result[0].Set(DATATYPE_INT); break; case SampleFormat.IEEEFP: result = new FieldValue[1]; result[0].Set(DATATYPE_IEEEFP); break; case SampleFormat.VOID: result = new FieldValue[1]; result[0].Set(DATATYPE_VOID); break; } break; case TiffTag.SAMPLEFORMAT: result = new FieldValue[1]; result[0].Set(td.td_sampleformat); break; case TiffTag.IMAGEDEPTH: result = new FieldValue[1]; result[0].Set(td.td_imagedepth); break; case TiffTag.SUBIFD: result = new FieldValue[2]; result[0].Set(td.td_nsubifd); result[1].Set(td.td_subifd); break; case TiffTag.YCBCRPOSITIONING: result = new FieldValue[1]; result[0].Set(td.td_ycbcrpositioning); break; case TiffTag.YCBCRSUBSAMPLING: result = new FieldValue[2]; result[0].Set(td.td_ycbcrsubsampling[0]); result[1].Set(td.td_ycbcrsubsampling[1]); break; case TiffTag.TRANSFERFUNCTION: result = new FieldValue[3]; result[0].Set(td.td_transferfunction[0]); if (td.td_samplesperpixel - td.td_extrasamples > 1) { result[1].Set(td.td_transferfunction[1]); result[2].Set(td.td_transferfunction[2]); } break; case TiffTag.REFERENCEBLACKWHITE: if (td.td_refblackwhite != null) { result = new FieldValue[1]; result[0].Set(td.td_refblackwhite); } break; case TiffTag.INKNAMES: result = new FieldValue[1]; result[0].Set(td.td_inknames); break; default: // This can happen if multiple images are open with // different codecs which have private tags. The global tag // information table may then have tags that are valid for // one file but not the other. If the client tries to get a // tag that is not valid for the image's codec then we'll // arrive here. TiffFieldInfo fip = tif.FindFieldInfo(tag, TiffType.ANY); if (fip == null || fip.Bit != FieldBit.Custom) { Tiff.ErrorExt(tif, tif.m_clientdata, "_TIFFVGetField", "{0}: Invalid {1}tag \"{2}\" (not supported by codec)", tif.m_name, Tiff.isPseudoTag(tag) ? "pseudo-" : string.Empty, fip != null ? fip.Name : "Unknown"); result = null; break; } // Do we have a custom value? result = null; for (int i = 0; i < td.td_customValueCount; i++) { TiffTagValue tv = td.td_customValues[i]; if (tv.info.Tag != tag) { continue; } if (fip.PassCount) { result = new FieldValue[2]; if (fip.ReadCount == TiffFieldInfo.Variable2) { result[0].Set(tv.count); } else { // Assume TiffFieldInfo.Variable result[0].Set(tv.count); } result[1].Set(tv.value); } else { if ((fip.Type == TiffType.ASCII || fip.ReadCount == TiffFieldInfo.Variable || fip.ReadCount == TiffFieldInfo.Variable2 || fip.ReadCount == TiffFieldInfo.Spp || tv.count > 1) && fip.Tag != TiffTag.PAGENUMBER && fip.Tag != TiffTag.HALFTONEHINTS && fip.Tag != TiffTag.YCBCRSUBSAMPLING && fip.Tag != TiffTag.DOTRANGE) { result = new FieldValue[1]; byte[] value = tv.value; if (fip.Type == TiffType.ASCII && tv.value.Length > 0 && tv.value[tv.value.Length - 1] == 0) { // cut unwanted zero at the end value = new byte[Math.Max(tv.value.Length - 1, 0)]; Buffer.BlockCopy(tv.value, 0, value, 0, value.Length); } result[0].Set(value); } else { result = new FieldValue[tv.count]; byte[] val = tv.value; int valPos = 0; for (int j = 0; j < tv.count; j++, valPos += Tiff.dataSize(tv.info.Type)) { switch (fip.Type) { case TiffType.BYTE: case TiffType.UNDEFINED: case TiffType.SBYTE: result[j].Set(val[valPos]); break; case TiffType.SHORT: case TiffType.SSHORT: result[j].Set(BitConverter.ToInt16(val, valPos)); break; case TiffType.LONG: case TiffType.IFD: case TiffType.SLONG: result[j].Set(BitConverter.ToInt32(val, valPos)); break; case TiffType.RATIONAL: case TiffType.SRATIONAL: case TiffType.FLOAT: result[j].Set(BitConverter.ToSingle(val, valPos)); break; case TiffType.DOUBLE: result[j].Set(BitConverter.ToDouble(val, valPos)); break; default: result = null; break; } } } } break; } break; } return(result); }
/// <summary> /// Sets the value(s) of a tag in a TIFF file/stream open for writing. /// </summary> /// <param name="tif">An instance of the <see cref="Tiff"/> class.</param> /// <param name="tag">The tag.</param> /// <param name="value">The tag value(s).</param> /// <returns> /// <c>true</c> if tag value(s) were set successfully; otherwise, <c>false</c>. /// </returns> /// <seealso cref="Tiff.SetField"/> public virtual bool SetField(Tiff tif, TiffTag tag, FieldValue[] value) { const string module = "vsetfield"; TiffDirectory td = tif.m_dir; bool status = true; int v32 = 0; int v = 0; bool end = false; bool badvalue = false; bool badvalue32 = false; switch (tag) { case TiffTag.SUBFILETYPE: td.td_subfiletype = (FileType)value[0].ToByte(); break; case TiffTag.IMAGEWIDTH: td.td_imagewidth = value[0].ToInt(); break; case TiffTag.IMAGELENGTH: td.td_imagelength = value[0].ToInt(); break; case TiffTag.BITSPERSAMPLE: td.td_bitspersample = value[0].ToShort(); // If the data require post-decoding processing to byte-swap samples, set it // up here. Note that since tags are required to be ordered, compression code // can override this behavior in the setup method if it wants to roll the post // decoding work in with its normal work. if ((tif.m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) { switch (td.td_bitspersample) { case 16: tif.m_postDecodeMethod = Tiff.PostDecodeMethodType.pdmSwab16Bit; break; case 24: tif.m_postDecodeMethod = Tiff.PostDecodeMethodType.pdmSwab24Bit; break; case 32: tif.m_postDecodeMethod = Tiff.PostDecodeMethodType.pdmSwab32Bit; break; case 64: tif.m_postDecodeMethod = Tiff.PostDecodeMethodType.pdmSwab64Bit; break; case 128: // two 64's tif.m_postDecodeMethod = Tiff.PostDecodeMethodType.pdmSwab64Bit; break; } } break; case TiffTag.COMPRESSION: v = value[0].ToInt() & 0xffff; Compression comp = (Compression)v; // If we're changing the compression scheme, then notify the previous module // so that it can cleanup any state it's setup. if (tif.fieldSet(FieldBit.Compression)) { if (td.td_compression == comp) { break; } tif.m_currentCodec.Cleanup(); tif.m_flags &= ~TiffFlags.CODERSETUP; } // Setup new compression scheme. status = tif.setCompressionScheme(comp); if (status) { td.td_compression = comp; } else { status = false; } break; case TiffTag.PHOTOMETRIC: td.td_photometric = (Photometric)value[0].ToInt(); break; case TiffTag.THRESHHOLDING: td.td_threshholding = (Threshold)value[0].ToByte(); break; case TiffTag.FILLORDER: v = value[0].ToInt(); FillOrder fo = (FillOrder)v; if (fo != FillOrder.LSB2MSB && fo != FillOrder.MSB2LSB) { badvalue = true; break; } td.td_fillorder = fo; break; case TiffTag.ORIENTATION: v = value[0].ToInt(); Orientation or = (Orientation)v; if (or < Orientation.TOPLEFT || Orientation.LEFTBOT < or) { badvalue = true; break; } else { td.td_orientation = or; } break; case TiffTag.SAMPLESPERPIXEL: // XXX should cross check - e.g. if pallette, then 1 v = value[0].ToInt(); if (v == 0) { badvalue = true; break; } td.td_samplesperpixel = (short)v; break; case TiffTag.ROWSPERSTRIP: v32 = value[0].ToInt(); if (v32 == 0) { badvalue32 = true; break; } td.td_rowsperstrip = v32; if (!tif.fieldSet(FieldBit.TileDimensions)) { td.td_tilelength = v32; td.td_tilewidth = td.td_imagewidth; } break; case TiffTag.MINSAMPLEVALUE: td.td_minsamplevalue = value[0].ToUShort(); break; case TiffTag.MAXSAMPLEVALUE: td.td_maxsamplevalue = value[0].ToUShort(); break; case TiffTag.SMINSAMPLEVALUE: td.td_sminsamplevalue = value[0].ToDouble(); break; case TiffTag.SMAXSAMPLEVALUE: td.td_smaxsamplevalue = value[0].ToDouble(); break; case TiffTag.XRESOLUTION: td.td_xresolution = value[0].ToFloat(); break; case TiffTag.YRESOLUTION: td.td_yresolution = value[0].ToFloat(); break; case TiffTag.PLANARCONFIG: v = value[0].ToInt(); PlanarConfig pc = (PlanarConfig)v; if (pc != PlanarConfig.CONTIG && pc != PlanarConfig.SEPARATE) { badvalue = true; break; } td.td_planarconfig = pc; break; case TiffTag.XPOSITION: td.td_xposition = value[0].ToFloat(); break; case TiffTag.YPOSITION: td.td_yposition = value[0].ToFloat(); break; case TiffTag.RESOLUTIONUNIT: v = value[0].ToInt(); ResUnit ru = (ResUnit)v; if (ru < ResUnit.NONE || ResUnit.CENTIMETER < ru) { badvalue = true; break; } td.td_resolutionunit = ru; break; case TiffTag.PAGENUMBER: td.td_pagenumber[0] = value[0].ToShort(); td.td_pagenumber[1] = value[1].ToShort(); break; case TiffTag.HALFTONEHINTS: td.td_halftonehints[0] = value[0].ToShort(); td.td_halftonehints[1] = value[1].ToShort(); break; case TiffTag.COLORMAP: v32 = 1 << td.td_bitspersample; Tiff.setShortArray(out td.td_colormap[0], value[0].ToShortArray(), v32); Tiff.setShortArray(out td.td_colormap[1], value[1].ToShortArray(), v32); Tiff.setShortArray(out td.td_colormap[2], value[2].ToShortArray(), v32); break; case TiffTag.EXTRASAMPLES: if (!setExtraSamples(td, ref v, value)) { badvalue = true; break; } break; case TiffTag.MATTEING: if (value[0].ToShort() != 0) { td.td_extrasamples = 1; } else { td.td_extrasamples = 0; } if (td.td_extrasamples != 0) { td.td_sampleinfo = new ExtraSample[1]; td.td_sampleinfo[0] = ExtraSample.ASSOCALPHA; } break; case TiffTag.TILEWIDTH: v32 = value[0].ToInt(); if ((v32 % 16) != 0) { if (tif.m_mode != Tiff.O_RDONLY) { badvalue32 = true; break; } Tiff.WarningExt(tif, tif.m_clientdata, tif.m_name, "Nonstandard tile width {0}, convert file", v32); } td.td_tilewidth = v32; tif.m_flags |= TiffFlags.ISTILED; break; case TiffTag.TILELENGTH: v32 = value[0].ToInt(); if ((v32 % 16) != 0) { if (tif.m_mode != Tiff.O_RDONLY) { badvalue32 = true; break; } Tiff.WarningExt(tif, tif.m_clientdata, tif.m_name, "Nonstandard tile length {0}, convert file", v32); } td.td_tilelength = v32; tif.m_flags |= TiffFlags.ISTILED; break; case TiffTag.TILEDEPTH: v32 = value[0].ToInt(); if (v32 == 0) { badvalue32 = true; break; } td.td_tiledepth = v32; break; case TiffTag.DATATYPE: v = value[0].ToInt(); SampleFormat sf = SampleFormat.VOID; switch (v) { case DATATYPE_VOID: sf = SampleFormat.VOID; break; case DATATYPE_INT: sf = SampleFormat.INT; break; case DATATYPE_UINT: sf = SampleFormat.UINT; break; case DATATYPE_IEEEFP: sf = SampleFormat.IEEEFP; break; default: badvalue = true; break; } if (!badvalue) { td.td_sampleformat = sf; } break; case TiffTag.SAMPLEFORMAT: v = value[0].ToInt(); sf = (SampleFormat)v; if (sf < SampleFormat.UINT || SampleFormat.COMPLEXIEEEFP < sf) { badvalue = true; break; } td.td_sampleformat = sf; // Try to fix up the SWAB function for complex data. if (td.td_sampleformat == SampleFormat.COMPLEXINT && td.td_bitspersample == 32 && tif.m_postDecodeMethod == Tiff.PostDecodeMethodType.pdmSwab32Bit) { tif.m_postDecodeMethod = Tiff.PostDecodeMethodType.pdmSwab16Bit; } else if ((td.td_sampleformat == SampleFormat.COMPLEXINT || td.td_sampleformat == SampleFormat.COMPLEXIEEEFP) && td.td_bitspersample == 64 && tif.m_postDecodeMethod == Tiff.PostDecodeMethodType.pdmSwab64Bit) { tif.m_postDecodeMethod = Tiff.PostDecodeMethodType.pdmSwab32Bit; } break; case TiffTag.IMAGEDEPTH: td.td_imagedepth = value[0].ToInt(); break; case TiffTag.SUBIFD: if ((tif.m_flags & TiffFlags.INSUBIFD) != TiffFlags.INSUBIFD) { td.td_nsubifd = value[0].ToInt(); Tiff.setLong8Array(out td.td_subifd, value[1].TolongArray(), td.td_nsubifd); } else { Tiff.ErrorExt(tif, tif.m_clientdata, module, "{0}: Sorry, cannot nest SubIFDs", tif.m_name); status = false; } break; case TiffTag.YCBCRPOSITIONING: td.td_ycbcrpositioning = (YCbCrPosition)value[0].ToShort(); break; case TiffTag.YCBCRSUBSAMPLING: td.td_ycbcrsubsampling[0] = value[0].ToShort(); td.td_ycbcrsubsampling[1] = value[1].ToShort(); break; case TiffTag.TRANSFERFUNCTION: v = ((td.td_samplesperpixel - td.td_extrasamples) > 1 ? 3 : 1); for (int i = 0; i < v; i++) { Tiff.setShortArray(out td.td_transferfunction[i], value[0].ToShortArray(), 1 << td.td_bitspersample); } break; case TiffTag.REFERENCEBLACKWHITE: // XXX should check for null range Tiff.setFloatArray(out td.td_refblackwhite, value[0].ToFloatArray(), 6); break; case TiffTag.INKNAMES: v = value[0].ToInt(); string s = value[1].ToString(); v = checkInkNamesString(tif, v, s); status = v > 0; if (v > 0) { setNString(out td.td_inknames, s, v); td.td_inknameslen = v; } break; default: // This can happen if multiple images are open with // different codecs which have private tags. The global tag // information table may then have tags that are valid for // one file but not the other. If the client tries to set a // tag that is not valid for the image's codec then we'll // arrive here. This happens, for example, when tiffcp is // used to convert between compression schemes and // codec-specific tags are blindly copied. TiffFieldInfo fip = tif.FindFieldInfo(tag, TiffType.ANY); if (fip == null || fip.Bit != FieldBit.Custom) { Tiff.ErrorExt(tif, tif.m_clientdata, module, "{0}: Invalid {1}tag \"{2}\" (not supported by codec)", tif.m_name, Tiff.isPseudoTag(tag) ? "pseudo-" : string.Empty, fip != null ? fip.Name : "Unknown"); status = false; break; } // Find the existing entry for this custom value. int tvIndex = -1; for (int iCustom = 0; iCustom < td.td_customValueCount; iCustom++) { if (td.td_customValues[iCustom].info.Tag == tag) { tvIndex = iCustom; td.td_customValues[iCustom].value = null; break; } } // Grow the custom list if the entry was not found. if (tvIndex == -1) { td.td_customValueCount++; TiffTagValue[] new_customValues = Tiff.Realloc( td.td_customValues, td.td_customValueCount - 1, td.td_customValueCount); td.td_customValues = new_customValues; tvIndex = td.td_customValueCount - 1; td.td_customValues[tvIndex].info = fip; td.td_customValues[tvIndex].value = null; td.td_customValues[tvIndex].count = 0; } // Set custom value ... save a copy of the custom tag value. int tv_size = Tiff.dataSize(fip.Type); if (tv_size == 0) { status = false; Tiff.ErrorExt(tif, tif.m_clientdata, module, "{0}: Bad field type {1} for \"{2}\"", tif.m_name, fip.Type, fip.Name); end = true; break; } int paramIndex = 0; if (fip.PassCount) { if (fip.WriteCount == TiffFieldInfo.Variable2) { td.td_customValues[tvIndex].count = value[paramIndex++].ToInt(); } else { td.td_customValues[tvIndex].count = value[paramIndex++].ToInt(); } } else if (fip.WriteCount == TiffFieldInfo.Variable || fip.WriteCount == TiffFieldInfo.Variable2) { td.td_customValues[tvIndex].count = 1; } else if (fip.WriteCount == TiffFieldInfo.Spp) { td.td_customValues[tvIndex].count = td.td_samplesperpixel; } else { td.td_customValues[tvIndex].count = fip.WriteCount; } if (fip.Type == TiffType.ASCII) { string ascii; Tiff.setString(out ascii, value[paramIndex++].ToString()); td.td_customValues[tvIndex].value = Tiff.Latin1Encoding.GetBytes(ascii); } else { td.td_customValues[tvIndex].value = new byte[tv_size * td.td_customValues[tvIndex].count]; if ((fip.PassCount || fip.WriteCount == TiffFieldInfo.Variable || fip.WriteCount == TiffFieldInfo.Variable2 || fip.WriteCount == TiffFieldInfo.Spp || td.td_customValues[tvIndex].count > 1) && fip.Tag != TiffTag.PAGENUMBER && fip.Tag != TiffTag.HALFTONEHINTS && fip.Tag != TiffTag.YCBCRSUBSAMPLING && fip.Tag != TiffTag.DOTRANGE) { byte[] apBytes = value[paramIndex++].GetBytes(); Buffer.BlockCopy(apBytes, 0, td.td_customValues[tvIndex].value, 0, Math.Min(apBytes.Length, td.td_customValues[tvIndex].value.Length)); } else { // XXX: The following loop required to handle // PAGENUMBER, HALFTONEHINTS, // YCBCRSUBSAMPLING and DOTRANGE tags. // These tags are actually arrays and should be // passed as arrays to SetField() function, but // actually passed as a list of separate values. // This behavior must be changed in the future! // Upd: This loop also processes some EXIF tags with // UNDEFINED type (like EXIF_FILESOURCE or EXIF_SCENETYPE) // In this case input value is string-based, so // in TiffType.UNDEFINED case we use FieldValue.GetBytes()[0] // construction instead of direct call of FieldValue.ToByte() method. byte[] val = td.td_customValues[tvIndex].value; int valPos = 0; for (int i = 0; i < td.td_customValues[tvIndex].count; i++, valPos += tv_size) { switch (fip.Type) { case TiffType.BYTE: case TiffType.UNDEFINED: val[valPos] = value[paramIndex + i].GetBytes()[0]; break; case TiffType.SBYTE: val[valPos] = value[paramIndex + i].ToByte(); break; case TiffType.SHORT: Buffer.BlockCopy(BitConverter.GetBytes(value[paramIndex + i].ToShort()), 0, val, valPos, tv_size); break; case TiffType.SSHORT: Buffer.BlockCopy(BitConverter.GetBytes(value[paramIndex + i].ToShort()), 0, val, valPos, tv_size); break; case TiffType.LONG: case TiffType.IFD: Buffer.BlockCopy(BitConverter.GetBytes(value[paramIndex + i].ToInt()), 0, val, valPos, tv_size); break; case TiffType.SLONG: Buffer.BlockCopy(BitConverter.GetBytes(value[paramIndex + i].ToInt()), 0, val, valPos, tv_size); break; case TiffType.RATIONAL: case TiffType.SRATIONAL: case TiffType.FLOAT: Buffer.BlockCopy(BitConverter.GetBytes(value[paramIndex + i].ToFloat()), 0, val, valPos, tv_size); break; case TiffType.DOUBLE: Buffer.BlockCopy(BitConverter.GetBytes(value[paramIndex + i].ToDouble()), 0, val, valPos, tv_size); break; default: Array.Clear(val, valPos, tv_size); status = false; break; } } } } break; } if (!end && !badvalue && !badvalue32) { if (status) { tif.setFieldBit(tif.FieldWithTag(tag).Bit); tif.m_flags |= TiffFlags.DIRTYDIRECT; } } if (badvalue) { Tiff.ErrorExt(tif, tif.m_clientdata, module, "{0}: Bad value {1} for \"{2}\" tag", tif.m_name, v, tif.FieldWithTag(tag).Name); return(false); } if (badvalue32) { Tiff.ErrorExt(tif, tif.m_clientdata, module, "{0}: Bad value {1} for \"{2}\" tag", tif.m_name, v32, tif.FieldWithTag(tag).Name); return(false); } return(status); }
private static TiffFieldInfo createAnonFieldInfo(TiffTag tag, TiffType field_type) { TiffFieldInfo fld = new TiffFieldInfo(tag, TiffFieldInfo.Variable2, TiffFieldInfo.Variable2, field_type, FieldBit.Custom, true, true, null); // note that this name is a special sign to Close() and // setupFieldInfo() to free the field fld.Name = string.Format(CultureInfo.InvariantCulture, "Tag {0}", tag); return fld; }
// Helper methods private static void TagExtender(Tiff tif) { TiffFieldInfo[] tiffFieldInfo = new TiffFieldInfo[1]; tiffFieldInfo[0] = new TiffFieldInfo((TiffTag)TagToWrite, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "Tag." + TagToWrite); tif.MergeFieldInfo(tiffFieldInfo, tiffFieldInfo.Length); }
private static void printField(Stream fd, TiffFieldInfo fip, int value_count, object raw_data) { fprintf(fd, " {0}: ", fip.Name); byte[] bytes = raw_data as byte[]; sbyte[] sbytes = raw_data as sbyte[]; short[] shorts = raw_data as short[]; ushort[] ushorts = raw_data as ushort[]; int[] ints = raw_data as int[]; uint[] uints = raw_data as uint[]; float[] floats = raw_data as float[]; double[] doubles = raw_data as double[]; string s = raw_data as string; for (int j = 0; j < value_count; j++) { if (fip.Type == TiffType.BYTE || fip.Type == TiffType.SBYTE) { if (bytes != null) { fprintf(fd, "{0}", bytes[j]); } else if (sbytes != null) { fprintf(fd, "{0}", sbytes[j]); } } else if (fip.Type == TiffType.UNDEFINED) { if (bytes != null) { fprintf(fd, "0x{0:x}", bytes[j]); } } else if (fip.Type == TiffType.SHORT || fip.Type == TiffType.SSHORT) { if (shorts != null) { fprintf(fd, "{0}", shorts[j]); } else if (ushorts != null) { fprintf(fd, "{0}", ushorts[j]); } } else if (fip.Type == TiffType.LONG || fip.Type == TiffType.SLONG) { if (ints != null) { fprintf(fd, "{0}", ints[j]); } else if (uints != null) { fprintf(fd, "{0}", uints[j]); } } else if (fip.Type == TiffType.RATIONAL || fip.Type == TiffType.SRATIONAL || fip.Type == TiffType.FLOAT) { if (floats != null) { fprintf(fd, "{0}", floats[j]); } } else if (fip.Type == TiffType.IFD) { if (ints != null) { fprintf(fd, "0x{0:x}", ints[j]); } else if (uints != null) { fprintf(fd, "0x{0:x}", uints[j]); } } else if (fip.Type == TiffType.ASCII) { if (s != null) { fprintf(fd, "{0}", s); } break; } else if (fip.Type == TiffType.DOUBLE || fip.Type == TiffType.FLOAT) { if (floats != null) { fprintf(fd, "{0}", floats[j]); } else if (doubles != null) { fprintf(fd, "{0}", doubles[j]); } } else { fprintf(fd, "<unsupported data type in printField>"); break; } if (j < value_count - 1) { fprintf(fd, ","); } } fprintf(fd, "\r\n"); }
/// <summary> /// Writes custom directory. See ticket #51. /// </summary> /// <param name="pdiroff">Output directory offset.</param> /// <returns><c>true</c> if succeeded; otherwise, <c>false</c></returns> private bool WriteCustomDirectory(out long pdiroff) { pdiroff = -1; if (m_mode == O_RDONLY) { return(true); } // Size the directory so that we can calculate offsets for the data // items that aren't kept in-place in each field. int nfields = 0; for (int b = 0; b <= FieldBit.Last; b++) { if (fieldSet(b) && b != FieldBit.Custom) { nfields += (b < FieldBit.SubFileType ? 2 : 1); } } nfields += m_dir.td_customValueCount; int dirsize = nfields * TiffDirEntry.SizeInBytes(m_header.tiff_version == TIFF_BIGTIFF_VERSION); TiffDirEntry[] data = new TiffDirEntry[nfields]; // Put the directory at the end of the file. m_diroff = (ulong)((seekFile(0, SeekOrigin.End) + 1) & ~1); m_dataoff = m_diroff + sizeof(short) + (ulong)dirsize + sizeof(int); if ((m_dataoff & 1) != 0) { m_dataoff++; } seekFile((long)m_dataoff, SeekOrigin.Begin); // Setup external form of directory entries and write data items. int[] fields = new int[FieldBit.SetLongs]; Buffer.BlockCopy(m_dir.td_fieldsset, 0, fields, 0, FieldBit.SetLongs * sizeof(int)); for (int fi = 0, nfi = m_nfields; nfi > 0; nfi--, fi++) { TiffFieldInfo fip = m_fieldinfo[fi]; // 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.Bit == FieldBit.Custom) { bool is_set = false; for (int ci = 0; ci < m_dir.td_customValueCount; ci++) { is_set |= (m_dir.td_customValues[ci].info == fip); } if (!is_set) { continue; } } else if (!fieldSet(fields, fip.Bit)) { continue; } if (fip.Bit != FieldBit.Custom) { resetFieldBit(fields, fip.Bit); } } // Write directory. short dircount = (short)nfields; pdiroff = (long)m_nextdiroff; if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) { // 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. for (int i = 0; i < dircount; i++) { TiffDirEntry dirEntry = data[i]; short temp = (short)dirEntry.tdir_tag; SwabShort(ref temp); dirEntry.tdir_tag = (TiffTag)(ushort)temp; temp = (short)dirEntry.tdir_type; SwabShort(ref temp); dirEntry.tdir_type = (TiffType)temp; SwabLong(ref dirEntry.tdir_count); SwabBigTiffValue(ref dirEntry.tdir_offset, m_header.tiff_version == TIFF_BIGTIFF_VERSION, false); } dircount = (short)nfields; SwabShort(ref dircount); int tempOff = (int)pdiroff; SwabLong(ref tempOff); pdiroff = tempOff; } seekFile((long)m_diroff, SeekOrigin.Begin); if (!writeShortOK(dircount)) { ErrorExt(this, m_clientdata, m_name, "Error writing directory count"); return(false); } if (!writeDirEntryOK(data, dirsize / TiffDirEntry.SizeInBytes(m_header.tiff_version == TIFF_BIGTIFF_VERSION), m_header.tiff_version == TIFF_BIGTIFF_VERSION)) { ErrorExt(this, m_clientdata, m_name, "Error writing directory contents"); return(false); } if (!writeIntOK((int)pdiroff)) { ErrorExt(this, m_clientdata, m_name, "Error writing directory link"); return(false); } return(true); }
/// <summary> /// Fetches a tag that is not handled by special case code. /// </summary> private bool fetchNormalTag(TiffDirEntry dir) { bool ok = false; TiffFieldInfo fip = FieldWithTag(dir.tdir_tag); if (dir.tdir_count > 1) { switch (dir.tdir_type) { case TiffType.BYTE: case TiffType.SBYTE: byte[] bytes = new byte [dir.tdir_count]; ok = fetchByteArray(dir, bytes); if (ok) { if (fip.PassCount) { ok = SetField(dir.tdir_tag, dir.tdir_count, bytes); } else { ok = SetField(dir.tdir_tag, bytes); } } break; case TiffType.SHORT: case TiffType.SSHORT: short[] shorts = new short [dir.tdir_count]; ok = fetchShortArray(dir, shorts); if (ok) { if (fip.PassCount) { ok = SetField(dir.tdir_tag, dir.tdir_count, shorts); } else { ok = SetField(dir.tdir_tag, shorts); } } break; case TiffType.LONG: case TiffType.SLONG: int[] ints = new int [dir.tdir_count]; ok = fetchLongArray(dir, ints); if (ok) { if (fip.PassCount) { ok = SetField(dir.tdir_tag, dir.tdir_count, ints); } else { ok = SetField(dir.tdir_tag, ints); } } break; case TiffType.RATIONAL: case TiffType.SRATIONAL: float[] rs = new float [dir.tdir_count]; ok = fetchRationalArray(dir, rs); if (ok) { if (fip.PassCount) { ok = SetField(dir.tdir_tag, dir.tdir_count, rs); } else { ok = SetField(dir.tdir_tag, rs); } } break; case TiffType.FLOAT: float[] fs = new float [dir.tdir_count]; ok = fetchFloatArray(dir, fs); if (ok) { if (fip.PassCount) { ok = SetField(dir.tdir_tag, dir.tdir_count, fs); } else { ok = SetField(dir.tdir_tag, fs); } } break; case TiffType.DOUBLE: double[] ds = new double [dir.tdir_count]; ok = fetchDoubleArray(dir, ds); if (ok) { if (fip.PassCount) { ok = SetField(dir.tdir_tag, dir.tdir_count, ds); } else { ok = SetField(dir.tdir_tag, ds); } } break; case TiffType.ASCII: case TiffType.UNDEFINED: // bit of a cheat... // Some vendors write strings w/o the trailing null // byte, so always append one just in case. string cp; ok = fetchString(dir, out cp) != 0; if (ok) { if (fip.PassCount) { ok = SetField(dir.tdir_tag, dir.tdir_count, cp); } else { ok = SetField(dir.tdir_tag, cp); } } break; } } else if (checkDirCount(dir, 1)) { int v32 = 0; // singleton value switch (dir.tdir_type) { case TiffType.BYTE: case TiffType.SBYTE: case TiffType.SHORT: case TiffType.SSHORT: // If the tag is also acceptable as a LONG or SLONG // then SetField will expect an int parameter // passed to it. // // NB: We use FieldWithTag here knowing that // it returns us the first entry in the table // for the tag and that that entry is for the // widest potential data type the tag may have. TiffType type = fip.Type; if (type != TiffType.LONG && type != TiffType.SLONG) { short v = (short)extractData(dir); if (fip.PassCount) { short[] a = new short[1]; a[0] = v; ok = SetField(dir.tdir_tag, 1, a); } else { ok = SetField(dir.tdir_tag, v); } break; } v32 = extractData(dir); if (fip.PassCount) { int[] a = new int[1]; a[0] = (int)v32; ok = SetField(dir.tdir_tag, 1, a); } else { ok = SetField(dir.tdir_tag, v32); } break; case TiffType.LONG: case TiffType.SLONG: case TiffType.IFD: v32 = extractData(dir); if (fip.PassCount) { int[] a = new int[1]; a[0] = (int)v32; ok = SetField(dir.tdir_tag, 1, a); } else { ok = SetField(dir.tdir_tag, v32); } break; case TiffType.RATIONAL: case TiffType.SRATIONAL: case TiffType.FLOAT: float f = (dir.tdir_type == TiffType.FLOAT ? fetchFloat(dir): fetchRational(dir)); if (fip.PassCount) { float[] a = new float[1]; a[0] = f; ok = SetField(dir.tdir_tag, 1, a); } else { ok = SetField(dir.tdir_tag, f); } break; case TiffType.DOUBLE: double[] ds = new double[1]; ok = fetchDoubleArray(dir, ds); if (ok) { if (fip.PassCount) { ok = SetField(dir.tdir_tag, 1, ds); } else { ok = SetField(dir.tdir_tag, ds[0]); } } break; case TiffType.ASCII: case TiffType.UNDEFINED: // bit of a cheat... string c; ok = fetchString(dir, out c) != 0; if (ok) { if (fip.PassCount) { ok = SetField(dir.tdir_tag, 1, c); } else { ok = SetField(dir.tdir_tag, c); } } break; } } return(ok); }
/// <summary> /// Writes tags that are not special cased. /// </summary> private bool writeNormalTag(ref TiffDirEntry dir, TiffFieldInfo fip) { short wc = fip.WriteCount; dir.tdir_tag = fip.Tag; dir.tdir_type = fip.Type; dir.tdir_count = wc; switch (fip.Type) { case TiffType.SHORT: case TiffType.SSHORT: if (fip.PassCount) { short[] wp; int wc2; if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); wp = result[1].ToShortArray(); dir.tdir_count = wc2; } else { // Assume TiffFieldInfo.Variable FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); wp = result[1].ToShortArray(); dir.tdir_count = wc; } if (!writeShortArray(ref dir, wp)) { return(false); } } else { if (wc == 1) { FieldValue[] result = GetField(fip.Tag); short sv = result[0].ToShort(); dir.tdir_offset = insertData(dir.tdir_type, sv); } else { FieldValue[] result = GetField(fip.Tag); short[] wp = result[0].ToShortArray(); if (!writeShortArray(ref dir, wp)) { return(false); } } } break; case TiffType.LONG: case TiffType.SLONG: case TiffType.IFD: if (fip.PassCount) { int[] lp; int wc2; if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); lp = result[1].ToIntArray(); dir.tdir_count = wc2; } else { // Assume TiffFieldInfo.Variable FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); lp = result[1].ToIntArray(); dir.tdir_count = wc; } if (!writeLongArray(ref dir, lp)) { return(false); } } else { if (wc == 1) { // XXX handle LONG->SHORT conversion FieldValue[] result = GetField(fip.Tag); dir.tdir_offset = result[0].ToUInt(); } else { int[] lp; FieldValue[] result = GetField(fip.Tag); lp = result[0].ToIntArray(); if (!writeLongArray(ref dir, lp)) { return(false); } } } break; case TiffType.RATIONAL: case TiffType.SRATIONAL: if (fip.PassCount) { float[] fp; int wc2; if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); fp = result[1].ToFloatArray(); dir.tdir_count = wc2; } else { // Assume TiffFieldInfo.Variable FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); fp = result[1].ToFloatArray(); dir.tdir_count = wc; } if (!writeRationalArray(ref dir, fp)) { return(false); } } else { if (wc == 1) { float[] fv = new float[1]; FieldValue[] result = GetField(fip.Tag); fv[0] = result[0].ToFloat(); if (!writeRationalArray(ref dir, fv)) { return(false); } } else { FieldValue[] result = GetField(fip.Tag); float[] fp = result[0].ToFloatArray(); if (!writeRationalArray(ref dir, fp)) { return(false); } } } break; case TiffType.FLOAT: if (fip.PassCount) { float[] fp; int wc2; if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); fp = result[1].ToFloatArray(); dir.tdir_count = wc2; } else { // Assume TiffFieldInfo.Variable FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); fp = result[1].ToFloatArray(); dir.tdir_count = wc; } if (!writeFloatArray(ref dir, fp)) { return(false); } } else { if (wc == 1) { float[] fv = new float[1]; FieldValue[] result = GetField(fip.Tag); fv[0] = result[0].ToFloat(); if (!writeFloatArray(ref dir, fv)) { return(false); } } else { FieldValue[] result = GetField(fip.Tag); float[] fp = result[0].ToFloatArray(); if (!writeFloatArray(ref dir, fp)) { return(false); } } } break; case TiffType.DOUBLE: if (fip.PassCount) { double[] dp; int wc2; if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); dp = result[1].ToDoubleArray(); dir.tdir_count = wc2; } else { // Assume TiffFieldInfo.Variable FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); dp = result[1].ToDoubleArray(); dir.tdir_count = wc; } if (!writeDoubleArray(ref dir, dp)) { return(false); } } else { if (wc == 1) { double[] dv = new double[1]; FieldValue[] result = GetField(fip.Tag); dv[0] = result[0].ToDouble(); if (!writeDoubleArray(ref dir, dv)) { return(false); } } else { FieldValue[] result = GetField(fip.Tag); double[] dp = result[0].ToDoubleArray(); if (!writeDoubleArray(ref dir, dp)) { return(false); } } } break; case TiffType.ASCII: { FieldValue[] result = GetField(fip.Tag); string cp; if (fip.PassCount) { cp = result[1].ToString(); } else { cp = result[0].ToString(); } byte[] stringBytes = Latin1Encoding.GetBytes(cp); // If this last character is a '\0' null char if (stringBytes.Length != 0 && stringBytes[stringBytes.Length - 1] == 0) { dir.tdir_count = stringBytes.Length; if (!writeByteArray(ref dir, stringBytes)) { return(false); } } else { // add zero ('\0') at the end of the byte array byte[] totalBytes = new byte[stringBytes.Length + 1]; Buffer.BlockCopy(stringBytes, 0, totalBytes, 0, stringBytes.Length); dir.tdir_count = totalBytes.Length; if (!writeByteArray(ref dir, totalBytes)) { return(false); } } } break; case TiffType.BYTE: case TiffType.SBYTE: if (fip.PassCount) { byte[] cp; int wc2; if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); cp = result[1].ToByteArray(); dir.tdir_count = wc2; } else { // Assume TiffFieldInfo.Variable FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); cp = result[1].ToByteArray(); dir.tdir_count = wc; } if (!writeByteArray(ref dir, cp)) { return(false); } } else { if (wc == 1) { byte[] cv = new byte[1]; FieldValue[] result = GetField(fip.Tag); cv[0] = result[0].ToByte(); if (!writeByteArray(ref dir, cv)) { return(false); } } else { FieldValue[] result = GetField(fip.Tag); byte[] cp = result[0].ToByteArray(); if (!writeByteArray(ref dir, cp)) { return(false); } } } break; case TiffType.UNDEFINED: { byte[] cp; int wc2; if (wc == TiffFieldInfo.Variable) { FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); cp = result[1].ToByteArray(); dir.tdir_count = wc; } else if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); cp = result[1].ToByteArray(); dir.tdir_count = wc2; } else { FieldValue[] result = GetField(fip.Tag); cp = result[0].ToByteArray(); } if (!writeByteArray(ref dir, cp)) { return(false); } } break; case TiffType.NOTYPE: break; } return(true); }
private void setupFieldInfo(TiffFieldInfo[] info, int n) { m_nfields = 0; MergeFieldInfo(info, n); }
/// <summary> /// Writes the contents of the current directory to the specified file. /// </summary> /// <remarks>This routine doesn't handle overwriting a directory with /// auxiliary storage that's been changed.</remarks> private bool writeDirectory(bool done) { if (m_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 ((m_flags & TiffFlags.POSTENCODE) == TiffFlags.POSTENCODE) { m_flags &= ~TiffFlags.POSTENCODE; if (!m_currentCodec.PostEncode()) { ErrorExt(this, m_clientdata, m_name, "Error post-encoding before directory write"); return(false); } } // shutdown encoder m_currentCodec.Close(); // Flush any data that might have been written by the // compression close+cleanup routines. if (m_rawcc > 0 && (m_flags & TiffFlags.BEENWRITING) == TiffFlags.BEENWRITING && !flushData1()) { ErrorExt(this, m_clientdata, m_name, "Error flushing data before directory write"); return(false); } if ((m_flags & TiffFlags.MYBUFFER) == TiffFlags.MYBUFFER && m_rawdata != null) { m_rawdata = null; m_rawcc = 0; m_rawdatasize = 0; } m_flags &= ~(TiffFlags.BEENWRITING | TiffFlags.BUFFERSETUP); } // Size the directory so that we can calculate offsets for the data // items that aren't kept in-place in each field. int nfields = 0; for (int b = 0; b <= FieldBit.Last; b++) { if (fieldSet(b) && b != FieldBit.Custom) { nfields += (b < FieldBit.SubFileType ? 2 : 1); } } nfields += m_dir.td_customValueCount; int dirsize = nfields * TiffDirEntry.SizeInBytes; TiffDirEntry[] data = new TiffDirEntry [nfields]; for (int i = 0; i < nfields; i++) { data[i] = new TiffDirEntry(); } // Directory hasn't been placed yet, put it at the end of the file // and link it into the existing directory structure. if (m_diroff == 0 && !linkDirectory()) { return(false); } m_dataoff = m_diroff + sizeof(short) + (uint)dirsize + sizeof(int); if ((m_dataoff & 1) != 0) { m_dataoff++; } seekFile(m_dataoff, SeekOrigin.Begin); m_curdir++; int dir = 0; // Setup external form of directory entries and write data items. int[] fields = new int[FieldBit.SetLongs]; Buffer.BlockCopy(m_dir.td_fieldsset, 0, fields, 0, FieldBit.SetLongs * sizeof(int)); // Write out ExtraSamples tag only if extra samples are present in the data. if (fieldSet(fields, FieldBit.ExtraSamples) && m_dir.td_extrasamples == 0) { resetFieldBit(fields, FieldBit.ExtraSamples); nfields--; dirsize -= TiffDirEntry.SizeInBytes; } // XXX for (int fi = 0, nfi = m_nfields; nfi > 0; nfi--, fi++) { TiffFieldInfo fip = m_fieldinfo[fi]; // 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.Bit == FieldBit.Custom) { bool is_set = false; for (int ci = 0; ci < m_dir.td_customValueCount; ci++) { is_set |= (m_dir.td_customValues[ci].info == fip); } if (!is_set) { continue; } } else if (!fieldSet(fields, fip.Bit)) { continue; } // Handle other fields. TiffTag tag = (TiffTag)FieldBit.Ignore; switch (fip.Bit) { case FieldBit.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() ? TiffTag.TILEOFFSETS : TiffTag.STRIPOFFSETS; if (tag != fip.Tag) { continue; } data[dir].tdir_tag = tag; data[dir].tdir_type = TiffType.LONG; data[dir].tdir_count = m_dir.td_nstrips; if (!writeLongArray(ref data[dir], m_dir.td_stripoffset)) { return(false); } break; case FieldBit.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() ? TiffTag.TILEBYTECOUNTS: TiffTag.STRIPBYTECOUNTS; if (tag != fip.Tag) { continue; } data[dir].tdir_tag = tag; data[dir].tdir_type = TiffType.LONG; data[dir].tdir_count = m_dir.td_nstrips; if (!writeLongArray(ref data[dir], m_dir.td_stripbytecount)) { return(false); } break; case FieldBit.RowsPerStrip: setupShortLong(TiffTag.ROWSPERSTRIP, ref data[dir], m_dir.td_rowsperstrip); break; case FieldBit.ColorMap: if (!writeShortTable(TiffTag.COLORMAP, ref data[dir], 3, m_dir.td_colormap)) { return(false); } break; case FieldBit.ImageDimensions: setupShortLong(TiffTag.IMAGEWIDTH, ref data[dir++], m_dir.td_imagewidth); setupShortLong(TiffTag.IMAGELENGTH, ref data[dir], m_dir.td_imagelength); break; case FieldBit.TileDimensions: setupShortLong(TiffTag.TILEWIDTH, ref data[dir++], m_dir.td_tilewidth); setupShortLong(TiffTag.TILELENGTH, ref data[dir], m_dir.td_tilelength); break; case FieldBit.Compression: setupShort(TiffTag.COMPRESSION, ref data[dir], (short)m_dir.td_compression); break; case FieldBit.Photometric: setupShort(TiffTag.PHOTOMETRIC, ref data[dir], (short)m_dir.td_photometric); break; case FieldBit.Position: if (!writeRationalPair(data, dir, TiffType.RATIONAL, TiffTag.XPOSITION, m_dir.td_xposition, TiffTag.YPOSITION, m_dir.td_yposition)) { return(false); } dir++; break; case FieldBit.Resolution: if (!writeRationalPair(data, dir, TiffType.RATIONAL, TiffTag.XRESOLUTION, m_dir.td_xresolution, TiffTag.YRESOLUTION, m_dir.td_yresolution)) { return(false); } dir++; break; case FieldBit.BitsPerSample: case FieldBit.MinSampleValue: case FieldBit.MaxSampleValue: case FieldBit.SampleFormat: if (!writePerSampleShorts(fip.Tag, ref data[dir])) { return(false); } break; case FieldBit.SMinSampleValue: case FieldBit.SMaxSampleValue: if (!writePerSampleAnys(sampleToTagType(), fip.Tag, ref data[dir])) { return(false); } break; case FieldBit.PageNumber: case FieldBit.HalftoneHints: case FieldBit.YCbCrSubsampling: if (!setupShortPair(fip.Tag, ref data[dir])) { return(false); } break; case FieldBit.InkNames: if (!writeInkNames(ref data[dir])) { return(false); } break; case FieldBit.TransferFunction: if (!writeTransferFunction(ref data[dir])) { return(false); } break; case FieldBit.SubIFD: // XXX: Always write this field using LONG type // for backward compatibility. data[dir].tdir_tag = fip.Tag; data[dir].tdir_type = TiffType.LONG; data[dir].tdir_count = m_dir.td_nsubifd; if (!writeLongArray(ref data[dir], m_dir.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 (data[dir].tdir_count > 0) { m_flags |= TiffFlags.INSUBIFD; m_nsubifd = (short)data[dir].tdir_count; if (data[dir].tdir_count > 1) { m_subifdoff = data[dir].tdir_offset; } else { m_subifdoff = m_diroff + sizeof(short) + (uint)dir * TiffDirEntry.SizeInBytes + sizeof(short) * 2 + sizeof(int); } } break; default: // XXX: Should be fixed and removed. if (fip.Tag == TiffTag.DOTRANGE) { if (!setupShortPair(fip.Tag, ref data[dir])) { return(false); } } else if (!writeNormalTag(ref data[dir], fip)) { return(false); } break; } dir++; if (fip.Bit != FieldBit.Custom) { resetFieldBit(fields, fip.Bit); } } // Write directory. short dircount = (short)nfields; uint diroff = m_nextdiroff; if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) { // 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. for (dir = 0; dircount != 0; dir++, dircount--) { short temp = (short)data[dir].tdir_tag; SwabShort(ref temp); data[dir].tdir_tag = (TiffTag)(ushort)temp; temp = (short)data[dir].tdir_type; SwabShort(ref temp); data[dir].tdir_type = (TiffType)temp; SwabLong(ref data[dir].tdir_count); SwabUInt(ref data[dir].tdir_offset); } dircount = (short)nfields; SwabShort(ref dircount); SwabUInt(ref diroff); } seekFile(m_diroff, SeekOrigin.Begin); if (!writeShortOK(dircount)) { ErrorExt(this, m_clientdata, m_name, "Error writing directory count"); return(false); } if (!writeDirEntryOK(data, dirsize / TiffDirEntry.SizeInBytes)) { ErrorExt(this, m_clientdata, m_name, "Error writing directory contents"); return(false); } if (!writeIntOK((int)diroff)) { ErrorExt(this, m_clientdata, m_name, "Error writing directory link"); return(false); } if (done) { FreeDirectory(); m_flags &= ~TiffFlags.DIRTYDIRECT; m_currentCodec.Cleanup(); // Reset directory-related state for subsequent directories. CreateDirectory(); } return(true); }
private static void printField(Stream fd, TiffFieldInfo fip, int value_count, object raw_data) { fprintf(fd, " {0}: ", fip.Name); byte[] bytes = raw_data as byte[]; sbyte[] sbytes = raw_data as sbyte[]; short[] shorts = raw_data as short[]; ushort[] ushorts = raw_data as ushort[]; int[] ints = raw_data as int[]; uint[] uints = raw_data as uint[]; float[] floats = raw_data as float[]; double[] doubles = raw_data as double[]; string s = raw_data as string; for (int j = 0; j < value_count; j++) { if (fip.Type == TiffType.BYTE || fip.Type == TiffType.SBYTE) { if (bytes != null) fprintf(fd, "{0}", bytes[j]); else if (sbytes != null) fprintf(fd, "{0}", sbytes[j]); } else if (fip.Type == TiffType.UNDEFINED) { if (bytes != null) fprintf(fd, "0x{0:x}", bytes[j]); } else if (fip.Type == TiffType.SHORT || fip.Type == TiffType.SSHORT) { if (shorts != null) fprintf(fd, "{0}", shorts[j]); else if (ushorts != null) fprintf(fd, "{0}", ushorts[j]); } else if (fip.Type == TiffType.LONG || fip.Type == TiffType.SLONG) { if (ints != null) fprintf(fd, "{0}", ints[j]); else if (uints != null) fprintf(fd, "{0}", uints[j]); } else if (fip.Type == TiffType.RATIONAL || fip.Type == TiffType.SRATIONAL || fip.Type == TiffType.FLOAT) { if (floats != null) fprintf(fd, "{0}", floats[j]); } else if (fip.Type == TiffType.IFD) { if (ints != null) fprintf(fd, "0x{0:x}", ints[j]); else if (uints != null) fprintf(fd, "0x{0:x}", uints[j]); } else if (fip.Type == TiffType.ASCII) { if (s != null) fprintf(fd, "{0}", s); break; } else if (fip.Type == TiffType.DOUBLE || fip.Type == TiffType.FLOAT) { if (floats != null) fprintf(fd, "{0}", floats[j]); else if (doubles != null) fprintf(fd, "{0}", doubles[j]); } else { fprintf(fd, "<unsupported data type in printField>"); break; } if (j < value_count - 1) fprintf(fd, ","); } fprintf(fd, "\r\n"); }