OnComponentSavePre and Multimedia Metadata

This example only deals with freshly uploaded jpg, gif and png multimedia objects and will do nothing if the event is raised by component edit. 

To detect that a multmedia component has just been uploaded we examine the component XML with XPath to get the UploadedMultimediaFilename. This field is only present when a component is uploaded and is null if the component is just being edited.

The multimedia file type can be found from the component XML with XPath to get the MultimediaType @xlink:title attribute.

If you only want to get the image width and height metadata then simply use a System.Drawing.Image object to load the image from the UploadedMultimediaFilename and get the image.Width and image.Height values.

Many years ago I worked on an image processing application for the reprographics industry so I knew that png and jpeg images could provide more metadata and so could open the files with a binary reader to extract the data directly. The GIF format does not provide a method for additional metadata but the PNG format can supply metadata suitable for the title and alt while the JPG format can hold Adobe Photoshop metadata. I had intended to extract metadata from other multimedia types but the requirement was cut.

 

/// <summary> Tridion Event triggered before a Component is saved
/// </summary>
/// <param name="oComponent">Component</param>
/// <param name="doneEditing">done Editing</param>
void _ITCMEvents.OnComponentSavePre(TDS.Component oComponent, bool doneEditing)
{
if (!oComponent.IsMultimediaComponent)
{
return;
} MSXML2.DOMDocument oXML;
MSXML2.IXMLDOMNode oUploadedMultimediaFilename;
oXML = GetNewDOMDocument();
oXML.loadXML(oComponent.GetXML(TDSDefines.XMLReadFilter.XMLReadAll));
oUploadedMultimediaFilename = oXML.selectSingleNode("//tcm:UploadedMultimediaFilename");
if ((oUploadedMultimediaFilename == null))
{
return;
} TraceEventHandlerCall("OnComponentSavePre", oComponent.Title, oComponent.ID);
LogEvent("New upload " + oUploadedMultimediaFilename.text, EnumSeverity.severityInfo);

String strMultimediaType;
strMultimediaType = oXML.selectSingleNode("//tcm:MultimediaType/@xlink:title").text;
strMultimediaType = strMultimediaType.Split(new char[] { ' ' })[0]; LogEvent("New upload type " + strMultimediaType, EnumSeverity.severityInfo); switch (strMultimediaType.ToLower())
{
case "jpeg":
ImportMetadataJPEG(ref oComponent, oUploadedMultimediaFilename.text);
break;
case "gif":
ImportMetadataGIF(ref oComponent, oUploadedMultimediaFilename.text);
break;
case "png":
ImportMetadataPNG(ref oComponent, oUploadedMultimediaFilename.text);
break;
case "bitmap":
//ImportMetadataBITMAP(ref oComponent, oUploadedMultimediaFilename.text);
break;
case "word":
//ImportMetadataWORD(ref oComponent, oUploadedMultimediaFilename.text);
break;
case "excel":
//ImportMetadataEXCEL(ref oComponent, oUploadedMultimediaFilename.text);
break;
case "powerpoint":
//ImportMetadataPOWERPOINT(ref oComponent, oUploadedMultimediaFilename.text);
break;
case "pdf":
//ImportMetadataPDF(ref oComponent, oUploadedMultimediaFilename.text);
break;
default:
return;
}
}
JPEG Image metadata
/// <summary>Import JPEG image metadata </summary>
/// <param name="oComponent">Multimedia component which contains the image</param>
/// <param name="filename">the JPEG image filename</param>
/// <remarks></remarks>
public static void ImportMetadataJPEG(ref TDS.Component oComponent, string filename)
{
// C# is LittleEndian, JPEG is BigEndian, so must read bytes and swap
// because BinaryReader ReadInt() only reads LittleEndian
int offset = 0;
uint segmentmarker = 0;
uint segmentsize = 0; int precision = 0;
uint height = 0;
uint width = 0; byte[] bytedata = {0x00, 0x00}; FileStream file = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Read);
BinaryReader br = new BinaryReader(file); bytedata[1] = br.ReadByte();
bytedata[0] = br.ReadByte();
segmentmarker = BitConverter.ToUInt16(bytedata, 0); if (segmentmarker != 0xffd8) // 0xffd8 - SOI Start of Image marker
{
LogEvent("File is not a JPG Image, bad SOI marker=" + segmentmarker, EnumSeverity.severityError);
br.Close();
file.Close();
return;
} bytedata[1] = br.ReadByte();
bytedata[0] = br.ReadByte();
segmentmarker = BitConverter.ToUInt16(bytedata, 0); if (segmentmarker != 0xffe0) // 0xffe0 - APP0 Application marker
{
LogEvent("File is not a JPG Image, bad APP0 marker=" + segmentmarker, EnumSeverity.severityError);
br.Close();
file.Close();
return;
} bytedata[1] = br.ReadByte();
bytedata[0] = br.ReadByte();
segmentsize = BitConverter.ToUInt16(bytedata, 0); offset = 2; // skip over all other markers to the get to start of frame marker
while (segmentmarker != 0xffc0)
{
offset += (int)segmentsize + 2;
file.Position = offset;
bytedata[1] = br.ReadByte();
bytedata[0] = br.ReadByte();
segmentmarker = BitConverter.ToUInt16(bytedata, 0);
bytedata[1] = br.ReadByte();
bytedata[0] = br.ReadByte();
segmentsize = BitConverter.ToUInt16(bytedata, 0);
//LogEvent("segment marker=0x" + String.Format("{0:x}", segmentmarker), EnumSeverity.severityError);
} precision = br.ReadByte();
bytedata[1] = br.ReadByte();
bytedata[0] = br.ReadByte();
height = BitConverter.ToUInt16(bytedata, 0); bytedata[1] = br.ReadByte();
bytedata[0] = br.ReadByte();
width = BitConverter.ToUInt16(bytedata, 0); br.Close();
file.Close(); TDS.ItemFields oMetadata;
oMetadata = (TDS.ItemFields)oComponent.MetadataFields["metadata"].value[1];
oMetadata["width"].value[1] = (int)width;
oMetadata["height"].value[1] = (int)height; //TODO: CJM 8 Nov 2006
//read photoshop metadata from section marker 0xffed for IPTC metadata }
GIF Image metadata
/// <summary>Import GIF image metadata </summary>
/// <param name="oComponent">Multimedia component which contains the image</param>
/// <param name="filename">the GIF image filename</param>
/// <remarks></remarks>
public static void ImportMetadataGIF(ref TDS.Component oComponent, string filename)
{ FileStream file = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Read);
BinaryReader br = new BinaryReader(file);
ASCIIEncoding enc = new System.Text.ASCIIEncoding(); String head = enc.GetString(br.ReadBytes(3));
String version = enc.GetString(br.ReadBytes(3));
int width = br.ReadUInt16();
int height = br.ReadUInt16();
br.Close();
file.Close(); if (head == "GIF")
{
TDS.ItemFields oMetadata;
oMetadata = (TDS.ItemFields)oComponent.MetadataFields["metadata"].value[1];
oMetadata["width"].value[1] = width;
oMetadata["height"].value[1] = height;
}
else
{
LogEvent("File is not a GIF Image",EnumSeverity.severityError);
} }
PNG Image metadata
/// <summary>Import PNG image metadata </summary>
/// <param name="oComponent">Multimedia component which contains the image</param>
/// <param name="filename">the PNG image filename</param>
/// <remarks></remarks>
public static void ImportMetadataPNG(ref TDS.Component oComponent, String filename)
{
// Note: PNG uses Bigendian integers ie MSB in the lowest byte, LSB in the highest byte
byte [] pngsignaturebytes = {0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a};
int offset = 8;
FileStream file = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Read);
BinaryReader br = new BinaryReader(file);
ASCIIEncoding enc = new System.Text.ASCIIEncoding();
String pngsignature = enc.GetString(pngsignaturebytes);
String signature = enc.GetString(br.ReadBytes(8)); if (signature != pngsignature)
{
LogEvent("File is not a PNG Image signature=" + signature, EnumSeverity.severityError);
br.Close();
file.Close();
return;
}
byte[] bytedata;
bytedata = br.ReadBytes(4);
Array.Reverse(bytedata);
uint chunksize = BitConverter.ToUInt32(bytedata, 0);
String chunkname = enc.GetString(br.ReadBytes(4)); // Note: C# uses LitteEndian integers but PNG uses BigEndian integers bytedata = br.ReadBytes(4);
Array.Reverse(bytedata);
uint width = BitConverter.ToUInt32(bytedata,0);
bytedata = br.ReadBytes(4);
Array.Reverse(bytedata);
uint height = BitConverter.ToUInt32(bytedata, 0); TDS.ItemFields oMetadata;
oMetadata = (TDS.ItemFields)oComponent.MetadataFields["metadata"].value[1];
oMetadata["width"].value[1] = (int)width;
oMetadata["height"].value[1] = (int)height; // Import PNG Metadata
String textfields;
String textkeyword;
String textvalue;
char[] separators = {(char)0x00}; // read png chunks
while (chunkname != "IEND")
{
offset += (int)chunksize + 12;
file.Position = offset;
bytedata = br.ReadBytes(4);
Array.Reverse(bytedata);
chunksize = BitConverter.ToUInt32(bytedata, 0);
chunkname = enc.GetString(br.ReadBytes(4));

if (chunkname == "tEXt")
{
textfields = enc.GetString(br.ReadBytes((int)chunksize));
textkeyword = textfields.Split(separators)[0];
textvalue = textfields.Split(separators)[1];
switch (textkeyword)
{
case "Title":
oMetadata["title"].value[1] = textvalue;
break;
case "Author":
break;
case "Description":
oMetadata["alt"].value[1] = textvalue;
break;
case "Copyright":
break;
case "Creation Time":
break;
case "Software":
break;
case "Disclaimer":
break;
case "Warning":
break;
case "Source":
break;
case "Comment":
break;
default:
break;
}
}
} br.Close();
file.Close();
}

Created: Thursday 27th May 2010 11:18 PM
Last Modified: Sunday 11th July 2010 6:58 PM LinkedIn link: View Chris Mills' profile on LinkedIn
Valid XHTML 1.0! Valid CSS! Level A conformance icon, W3C-WAI Web Content Accessibility Guidelines 1.0