// Module: TIFFTAGS.cs - Developed By: Arvo Bowen III - Version: 2012.02.25
// References: BitMiracle.LibTiff.NET (BitMiracle.LibTiff.NET.dll)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BitMiracle.LibTiff.Classic;
using System.Diagnostics;
using System.IO;
namespace LibNetDotNetSupport
{
#region TIFFTAGS_TAG Class
public enum TIFFTAGS_TAG_DATATYPE { UNKNOWN = 0, ASCII = 2, SHORT = 3, LONG = 4, RATIONAL = 5 };
public class TIFFTAGS_TAG
{
private TIFFTAGS_TAG_DATATYPE m_dtTagType;
private Object m_oValue = null;
private int m_iTagNumber = -1;
private string m_sFileName = null;
private int m_iPageNumber = -1;
private string m_sTagName = null;
//Constructor
public TIFFTAGS_TAG()
{
TIFFTAGS_TAG_CONSTRUCTOR(null, -1, null);
}
public TIFFTAGS_TAG(string sFileName, int iTagNumber, Object oValue)
{
TIFFTAGS_TAG_CONSTRUCTOR(sFileName, iTagNumber, oValue);
}
private void TIFFTAGS_TAG_CONSTRUCTOR(string sFileName, int iTagNumber, Object oValue)
{
m_sFileName = sFileName;
m_iTagNumber = iTagNumber;
m_oValue = oValue;
}
//TagType
public TIFFTAGS_TAG_DATATYPE TagType
{
get
{
//Determine the data type
string sDataType = "";
if (m_oValue.GetType().ToString() == "BitMiracle.LibTiff.Classic.FieldValue[]")
{
//Data type is a field value
FieldValue[] fvValue = (FieldValue[])m_oValue;
sDataType = fvValue[0].Value.GetType().ToString();
}
else
{
//Try to check a normal system data type because this one is not a field value
sDataType = m_oValue.GetType().ToString();
}
switch (sDataType)
{
case "System.Int32":
case "System.UInt32[]":
case "BitMiracle.LibTiff.Classic.FileType":
m_dtTagType = TIFFTAGS_TAG_DATATYPE.LONG;
break;
case "System.Int16":
case "BitMiracle.LibTiff.Classic.Compression":
case "BitMiracle.LibTiff.Classic.Photometric":
case "BitMiracle.LibTiff.Classic.Threshold":
case "BitMiracle.LibTiff.Classic.FillOrder":
case "BitMiracle.LibTiff.Classic.Orientation":
case "BitMiracle.LibTiff.Classic.PlanarConfig":
case "BitMiracle.LibTiff.Classic.ResUnit":
m_dtTagType = TIFFTAGS_TAG_DATATYPE.SHORT;
break;
case "System.String":
case "System.Byte[]":
m_dtTagType = TIFFTAGS_TAG_DATATYPE.ASCII;
break;
case "System.Single":
m_dtTagType = TIFFTAGS_TAG_DATATYPE.RATIONAL;
break;
default:
m_dtTagType = TIFFTAGS_TAG_DATATYPE.UNKNOWN;
//Debug.WriteLine("Unknown TAG data type:" + fvValue[0].Value.GetType().ToString());
break;
}
return m_dtTagType;
}
}
//Value
public Object Value
{
get
{
return m_oValue;
}
set
{
m_oValue = value;
}
}
//TagNumber
public int TagNumber
{
get
{
return m_iTagNumber;
}
set
{
m_iTagNumber = value;
}
}
//FileName
public string FileName
{
get
{
return m_sFileName;
}
set
{
m_sFileName = value;
}
}
//PageNumber
public int PageNumber
{
get
{
return m_iPageNumber;
}
set
{
m_iPageNumber = value;
}
}
//TagName
public string TagName
{
get
{
if (m_iTagNumber >= 0)
{
switch (m_iTagNumber)
{
case 37572:
//GSCCCA Previous Page Tag
m_sTagName = "GSCCCA_PREVIOUS_PAGE";
break;
case 37574:
//GSCCCA Next Page Tag
m_sTagName = "GSCCCA_NEXT_PAGE";
break;
default:
//Convert the tag number to a tag name
TiffTag tTag = (TiffTag)m_iTagNumber;
m_sTagName = Enum.GetName(typeof(TiffTag), tTag);
if (m_sTagName == null) m_sTagName = "UNKNOWN_TAG_NAME";
break;
}
}
else
{
//No tag number has been set
m_sTagName = "NO_TAG_NUMBER_SET";
}
//Check if a tag name was not found
if (m_sTagName == "") m_sTagName = "UNKNOWN_TAG_NAME";
return m_sTagName;
}
}
public string ValueToFlatString()
{
string sValue = "";
if (m_oValue != null)
{
FieldValue[] fvValue = (FieldValue[])m_oValue;
//Determine what type of value is being unpacked
string sValueType = "";
switch (fvValue[0].Value.GetType().ToString())
{
case "System.Byte[]":
sValueType = "ascii";
break;
case "BitMiracle.LibTiff.Classic.FileType":
case "System.Int32":
if (fvValue.Count() == 2)
{
if (fvValue[1].Value.GetType().ToString() == "System.Byte[]") sValueType = "ascii";
}
else sValueType = "long";
break;
case "System.Int16":
case "BitMiracle.LibTiff.Classic.Photometric":
case "BitMiracle.LibTiff.Classic.Threshold":
case "BitMiracle.LibTiff.Classic.FillOrder":
case "BitMiracle.LibTiff.Classic.Orientation":
case "BitMiracle.LibTiff.Classic.PlanarConfig":
case "BitMiracle.LibTiff.Classic.ResUnit":
sValueType = "short";
break;
case "BitMiracle.LibTiff.Classic.Compression":
sValueType = "compression";
break;
case "System.UInt32[]":
sValueType = "to_long";
break;
case "System.Single":
sValueType = "rational";
break;
default:
sValueType = fvValue[0].Value.GetType().ToString();
break;
}
if (fvValue[0].Value != null)
{
switch (sValueType)
{
case "short":
sValue = fvValue[0].ToShort().ToString();
if (fvValue[0].ToShort().ToString() != fvValue[0].Value.ToString())
{
sValue += " [" + fvValue[0].Value.ToString() + "]";
}
break;
case "long":
sValue = fvValue[0].ToUInt().ToString();
if (fvValue[0].ToUInt().ToString() != fvValue[0].Value.ToString())
{
sValue += " [" + fvValue[0].Value.ToString() + "]";
}
break;
case "compression":
//Known short value
switch (fvValue[0].Value.ToString())
{
case "CCITT_T4":
//Tiff G3 custom string
sValue = "Group 3 Fax (CCITT T.4) [" + fvValue[0].ToShort() + "]";
break;
case "CCITT_T6":
//Tiff G4 custom string
sValue = "Group 4 Fax (CCITT T.6) [" + fvValue[0].ToShort() + "]";
break;
default:
//All other values
sValue = fvValue[0].ToShort().ToString();
if (fvValue[0].ToShort().ToString() != fvValue[0].Value.ToString())
{
sValue += " [" + fvValue[0].Value.ToString() + "]";
}
break;
}
break;
case "to_long":
//This will be a long value when all parts of the array are combined
foreach (UInt32 oValue in (UInt32[])fvValue[0].Value)
{
sValue += (oValue.ToString() != "" ? oValue.ToString() : "0");
}
break;
case "ascii":
//Byte array to be converted to a string (make sure to trim all the null characters off the end)
sValue = System.Text.ASCIIEncoding.ASCII.GetString((Byte[])fvValue[(fvValue.Count() - 1)].Value).TrimEnd(\'\\0\');
break;
case "rational":
sValue = ToFraction(Convert.ToDecimal(fvValue[0].Value.ToString()));
break;
default:
//All other values are assumed to be available using the ToString() methods
Debug.WriteLine(fvValue[0].Value.GetType().ToString());
sValue = "TYPE NOT SUPPORTED" + fvValue[0].Value.ToString();
break;
}
}
}
return sValue;
}
private static string ToFraction(decimal value)
{
// get the whole value of the fraction
decimal mWhole = Math.Truncate(value);
// get the fractional value
decimal mFraction = value - mWhole;
// initialize a numerator and denomintar
uint mNumerator = 0;
uint mDenomenator = 1;
// ensure that there is actual a fraction
if (mFraction > 0m)
{
// convert the value to a string so that you can count the number of decimal places there are
string strFraction = mFraction.ToString().Remove(0, 2);
// store teh number of decimal places
uint intFractLength = (uint)strFraction.Length;
// set the numerator to have the proper amount of zeros
mNumerator = (uint)Math.Pow(10, intFractLength);
// parse the fraction value to an integer that equals [fraction value] * 10^[number of decimal places]
uint.TryParse(strFraction, out mDenomenator);
// get the greatest common divisor for both numbers
uint gcd = GreatestCommonDivisor(mDenomenator, mNumerator);
// divide the numerator and the denominator by the gratest common divisor
mNumerator = mNumerator / gcd;
mDenomenator = mDenomenator / gcd;
}
// create a string builder
StringBuilder mBuilder = new StringBuilder();
// add the whole number if it\'s greater than 0
if (mWhole > 0m)
{
mBuilder.Append(mWhole);
}
// add the fraction if it\'s greater than 0m
if (mFraction > 0m)
{
if (mBuilder.Length > 0)
{
mBuilder.Append(" ");
}
mBuilder.Append(mDenomenator);
mBuilder.Append("/");
mBuilder.Append(mNumerator);
}
// add the whole number over 1 if the number is not a decimal
if (!mBuilder.ToString().Contains(\'/\')) mBuilder.Append("/1");
return mBuilder.ToString();
}
private static decimal ToDecimal(string value)
{
String[] sSplit = value.Split(\'/\');
if (sSplit.Length != 2) return -1;
try
{
decimal dNumerator = System.Convert.ToInt32(sSplit[0]);
decimal dDenominator = System.Convert.ToInt32(sSplit[1]);
return (dNumerator / dDenominator);
}
catch
{
//Error
return -1;
}
}
private static uint GreatestCommonDivisor(uint valA, uint valB)
{
// return 0 if both values are 0 (no GSD)
if (valA == 0 &&
valB == 0)
{
return 0;
}
// return value b if only a == 0
else if (valA == 0 &&
valB != 0)
{
return valB;
}
// return value a if only b == 0
else if (valA != 0 && valB == 0)
{
return valA;
}
// actually find the GSD
else
{
uint first = valA;
uint second = valB;
while (first != second)
{
if (first > second)
{
first = first - second;
}
else
{
second = second - first;
}
}
return first;
}
}
}
#endregion
public static class TIFFTAGS
{
#region TIFFTAGS_PAGE Class
private class TIFFTAGS_PAGE
{
private int m_iHeight;
private Dictionary<ushort, FieldValue[]> m_dTags;
private byte[] m_bytePageData;
private bool m_bEncoded;
private int m_iStripSize;
private int m_iStripOffset;
//Constructor
public TIFFTAGS_PAGE()
{
m_iHeight = 0;
m_dTags = new Dictionary<ushort, FieldValue[]>();
m_bytePageData = null;
m_bEncoded = false;
m_iStripSize = 0;
}
//Height
public int Height
{
get
{
return m_iHeight;
}
set
{
m_iHeight = value;
}
}
//Tags
public Dictionary<ushort, FieldValue[]> Tags
{
get
{
return m_dTags;
}
set
{
m_dTags = value;
}
}
//PageData
public byte[] PageData
{
get
{
return m_bytePageData;
}
set
{
m_bytePageData = value;
}
}
//Encoded
public bool Encoded
{
get
{
return m_bEncoded;
}
set
{
m_bEncoded = value;
}
}
//StripSize
public int StripSize
{
get
{
return m_iStripSize;
}
set
{
m_iStripSize = value;
}
}
//StripOffset
public int StripOffset
{
get
{
return m_iStripOffset;
}
set
{
m_iStripOffset = value;
}
}
}
#endregion
private static Tiff.TiffExtendProc m_parentExtender;
private static List<TIFFTAGS_TAG> m_lTagsToWrite;
private static void TagExtender(Tiff tif)
{
List<TiffFieldInfo> tfInfo = new List<TiffFieldInfo>();
Int16 readCount = 0;
Int16 writeCount = 0;
bool okToChange = false;
bool passCount = false;
//Make sure to create a new tiff field info for all the tags to be written
foreach (TIFFTAGS_TAG ttTag in m_lTagsToWrite)
{
switch (ttTag.TagType)
{
case TIFFTAGS_TAG_DATATYPE.ASCII:
readCount = -1;
writeCount = -1;
okToChange = true;
passCount = false;
break;
case TIFFTAGS_TAG_DATATYPE.LONG:
case TIFFTAGS_TAG_DATATYPE.RATIONAL:
case TIFFTAGS_TAG_DATATYPE.SHORT:
readCount = 2;
writeCount = 2;
okToChange = false;
passCount = true;
break;
}
if (ttTag.TagType != TIFFTAGS_TAG_DATATYPE.UNKNOWN)
{
//Add the new item to the list
tfInfo.Add(new TiffFieldInfo((TiffTag)ttTag.TagNumber, readCount, writeCount, (TiffType)ttTag.TagType, FieldBit.Custom, okToChange, passCount, ttTag.TagName));
}
}
//Turn the list into an array again
TiffFieldInfo[] tfInfoArray = tfInfo.ToArray();
//Add the fields to the main field info area
tif.MergeFieldInfo(tfInfoArray, tfInfoArray.Length);
//Propergate the chain if needed
if (m_parentExtender != null)
m_parentExtender(tif);
}
public static bool DeleteTiffTag(string sFileName, ushort ushortTagNumber)
{
//Deletes a tiff tag from the given image
//Returns true if successful or false if error occured
try
{
//Reuse the main DeleteTiffTags function
return DeleteTiffTags(sFileName, new List<ushort>(new ushort[] { ushortTagNumber }));
}
catch { return false; }
}
public static bool DeleteTiffTags(string sFileName, List<ushort> ushortTagNumbers)
{
//Deletes a list of tiff tag from the given image
//Returns true if successful or false if error occured
//Define variables
List<TIFFTAGS_PAGE> ttPage = new List<TIFFTAGS_PAGE>();
//Check for empty list
if (ushortTagNumbers.Count == 0) return false;
try
{
//ADDED
m_lTagsToWrite = new List<TIFFTAGS_TAG>();
m_lTagsToWrite.Add(new TIFFTAGS_TAG("", 38001, Convert.ToString("")));
m_lTagsToWrite.Add(new TIFFTAGS_TAG("", 38002, Convert.ToString("")));
m_parentExtender = Tiff.SetTagExtender(TagExtender);
//Open the file for reading
using (Tiff input = Tiff.Open(sFileName, "r"))
{
if (input == null) return false;
//Get page count
int numberOfDirectories = input.NumberOfDirectories();
//Go through all the pages
for (short i = 0; i < numberOfDirectories; ++i)
{
//Set the page
input.SetDirectory(i);
//Create a new tags dictionary to store all my tags
Dictionary<ushort, FieldValue[]> dTags = new Dictionary<ushort, FieldValue[]>();
//Get all the tags for the page
for (ushort t = ushort.MinValue; t < ushort.MaxValue; ++t)
{
TiffTag tag = (TiffTag)t;
FieldValue[] tagValue = input.GetField(tag);
if (tagValue != null)
{
dTags.Add(t, tagValue);
}
}
//Check if the page is encoded
bool encoded = false;
FieldValue[] compressionTagValue = input.GetField(TiffTag.COMPRESSION);
if (compressionTagValue != null)
encoded = (compressionTagValue[0].ToInt() != (int)Compression.NONE);
//Create a new byte array to store all my image data
int numberOfStrips = input.NumberOfStrips();
byte[] byteImageData = new byte[numberOfStrips * input.StripSize()];
int offset = 0;
//Get all the image data for the page
for (int n = 0; n < numberOfStrips; ++n)
{
int bytesRead;
if (encoded)
bytesRead = input.ReadEncodedStrip(n, byteImageData, offset, byteImageData.Length - offset);
else
bytesRead = input.ReadRawStrip(n, byteImageData, offset, byteImageData.Length - offset);
//Add to the offset keeping up with where we are
offset += bytesRead;
}
//Save all the tags, image data, and height, etc for the page
TIFFTAGS_PAGE tiffPage = new TIFFTAGS_PAGE();
tiffPage.Height = input.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
tiffPage.Tags = dTags;
tiffPage.PageData = byteImageData;
tiffPage.Encoded = encoded;
tiffPage.StripSize = input.StripSize();
tiffPage.StripOffset = input.GetField(TiffTag.STRIPOFFSETS)[0].ToIntArray()[0];
ttPage.Add(tiffPage);
}
}
//Open the file for writing
using (Tiff output = Tiff.Open(sFileName + "-new.tif", "w"))
{
if (output == null) return false;
//Go through all the pages
for (short i = 0; i < ttPage.Count(); ++i)
{
//Write all the tags for the page
foreach (KeyValuePair<ushort, FieldValue[]> tagValue in ttPage[i].Tags)
{
//Write all the tags except the one\'s needing to be deleted
if (!ushortTagNumbers.Contains(tagValue.Key))
{
TiffTag tag = (TiffTag)tagValue.Key;
output.GetTagMethods().SetField(output, tag, tagValue.Value);
}
}
//Set the height for the page
output.SetField(TiffTag.ROWSPERSTRIP, ttPage[i].Height);
//Set the offset for the page
output.SetField(TiffTag.STRIPOFFSETS, ttPage[i].StripOffset);
//Save page data along with tags
output.CheckpointDirectory();
//Write each strip one at a time using the same orginal strip size
int numberOfStrips = ttPage[i].PageData.Length / ttPage[i].StripSize;
int offset = 0;
for (int n = 0; n < numberOfStrips; ++n)
{
//Write all the image data (strips) for the page
if (ttPage[i].Encoded)
//output.WriteEncodedStrip(n, byteStrip, offset, byteStrip.Length - offset);
output.WriteEncodedStrip(0, ttPage[i].PageData, offset, ttPage[i].StripSize - offset);
else
output.WriteRawStrip(n, ttPage[i].PageData, offset, ttPage[i].StripSize - offset);
//Add to the offset keeping up with where we are
offset += ttPage[i].StripOffset;
}
//Save the image page
output.WriteDirectory();
}
}
//ADDED
Tiff.SetTagExtender(m_parentExtender);
}
catch
{
//ADDED
Tiff.SetTagExtender(m_parentExtender);
//Error occured
return false;
}
//Return success
return true;
}
}
}