Page 1 of 1 1
Topic Options
#210354 - 2015-06-17 11:36 AM Reading Image Headers to Get Width and Height
MACE Offline
Starting to like KiXtart

Registered: 2004-09-07
Posts: 150
Loc: Manchester UK
I have a need to determine the size of images in order to format the layout correctly on the fly in the script. Nothing identifiable as a UDF so on reviewing the manual on kixtart the binary read of a file header does not look possible directly so looks like we will need to be able to query common libraries like .net to get the job done. As my coding expertise is narrow I was wondering if anyone could advise on HOW !
Research brought up the following .net 2 method but I am unsure looking through it whether it is possible to implement from within KIXtart.

As always my hands are tied regarding compiling and distributing executable files so it needs to be a script solution. Compiling C# on the fly is possible but not likely to work in all situations. KIX is part of the base build as is .net so here's hoping we can declare and implement the necessary .net functions!

 Code:
/// <summary>
    /// http://www.codeproject.com/Articles/35978/Reading-Image-Headers-to-Get-Width-and-Height
    /// Taken from http://stackoverflow.com/questions/111345/getting-image-dimensions-without-reading-the-entire-file/111349
    /// Minor improvements including supporting unsigned 16-bit integers when decoding Jfif and added logic
    /// to load the image using new Bitmap if reading the headers fails
    /// </summary>
    public static class ImageHeader
    {
        private delegate TResult Func<T, TResult>(T arg);
        private const string errorMessage = "Could not recognise image format.";
 
        private static Dictionary<byte[], Func<BinaryReader, Size>> imageFormatDecoders = new Dictionary<byte[], Func<BinaryReader, Size>>()
        { 
            { new byte[] { 0x42, 0x4D }, DecodeBitmap }, 
            { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif }, 
            { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, DecodeGif }, 
            { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, DecodePng },
            { new byte[] { 0xff, 0xd8 }, DecodeJfif }, 
        };
 
        /// <summary>        
        /// Gets the dimensions of an image.        
        /// </summary>        
        /// <param name="path">The path of the image to get the dimensions of.</param>        
        /// <returns>The dimensions of the specified image.</returns>        
        /// <exception cref="ArgumentException">The image was of an unrecognised format.</exception>        
        public static Size GetDimensions(string path)
        {
            try
            {
                using (BinaryReader binaryReader = new BinaryReader(File.OpenRead(path)))
                {
                    try
                    {
                        return GetDimensions(binaryReader);
                    }
                    catch (ArgumentException e)
                    {
                        string newMessage = string.Format("{0} file: '{1}' ", errorMessage, path);
 
                        throw new ArgumentException(newMessage, "path", e);
                    }
                }
            }
            catch (ArgumentException)
            {
                //do it the old fashioned way

                using (Bitmap b = new Bitmap(path))
                {
                    return b.Size;
                }
            }
        }
 
        public static int GetMaxMagicBytesLength()
        {
            int maxMagicBytesLength = 0;
 
            foreach (byte[] magicBytes in imageFormatDecoders.Keys)
                maxMagicBytesLength = Math.Max(maxMagicBytesLength, magicBytes.Length);
 
            return maxMagicBytesLength;
        }
 
        /// <summary>        
        /// Gets the dimensions of an image.        
        /// </summary>        
        /// <param name="path">The path of the image to get the dimensions of.</param>        
        /// <returns>The dimensions of the specified image.</returns>        
        /// <exception cref="ArgumentException">The image was of an unrecognised format.</exception>            
        public static Size GetDimensions(BinaryReader binaryReader)
        {
            int maxMagicBytesLength = GetMaxMagicBytesLength();
 
            byte[] magicBytes = new byte[maxMagicBytesLength];
 
            for (int i = 0; i < maxMagicBytesLength; i += 1)
            {
                magicBytes[i] = binaryReader.ReadByte();
 
                foreach (var kvPair in imageFormatDecoders)
                {
                    if (StartsWith(magicBytes, kvPair.Key))
                    {
                        return kvPair.Value(binaryReader);
                    }
                }
            }
 
            throw new ArgumentException(errorMessage, "binaryReader");
        }
 
        private static bool StartsWith(byte[] thisBytes, byte[] thatBytes)
        {
            for (int i = 0; i < thatBytes.Length; i += 1)
            {
                if (thisBytes[i] != thatBytes[i])
                {
                    return false;
                }
            }
 
            return true;
        }
 
        private static short ReadLittleEndianInt16(BinaryReader binaryReader)
        {
            byte[] bytes = new byte[sizeof(short)];
 
            for (int i = 0; i < sizeof(short); i += 1)
            {
                bytes[sizeof(short) - 1 - i] = binaryReader.ReadByte();
            }
            return BitConverter.ToInt16(bytes, 0);
        }
 
        private static ushort ReadLittleEndianUInt16(BinaryReader binaryReader)
        {
            byte[] bytes = new byte[sizeof(ushort)];
 
            for (int i = 0; i < sizeof(ushort); i += 1)
            {
                bytes[sizeof(ushort) - 1 - i] = binaryReader.ReadByte();
            }
            return BitConverter.ToUInt16(bytes, 0);
        }
 
        private static int ReadLittleEndianInt32(BinaryReader binaryReader)
        {
            byte[] bytes = new byte[sizeof(int)];
            for (int i = 0; i < sizeof(int); i += 1)
            {
                bytes[sizeof(int) - 1 - i] = binaryReader.ReadByte();
            }
            return BitConverter.ToInt32(bytes, 0);
        }
 
        private static Size DecodeBitmap(BinaryReader binaryReader)
        {
            binaryReader.ReadBytes(16);
            int width = binaryReader.ReadInt32();
            int height = binaryReader.ReadInt32();
            return new Size(width, height);
        }
 
        private static Size DecodeGif(BinaryReader binaryReader)
        {
            int width = binaryReader.ReadInt16();
            int height = binaryReader.ReadInt16();
            return new Size(width, height);
        }
 
        private static Size DecodePng(BinaryReader binaryReader)
        {
            binaryReader.ReadBytes(8);
            int width = ReadLittleEndianInt32(binaryReader);
            int height = ReadLittleEndianInt32(binaryReader);
            return new Size(width, height);
        }
 
        private static Size DecodeJfif(BinaryReader binaryReader)
        {
            while (binaryReader.ReadByte() == 0xff)
            {
                byte marker = binaryReader.ReadByte();
                short chunkLength = ReadLittleEndianInt16(binaryReader);
                if (marker == 0xc0)
                {
                    binaryReader.ReadByte();
                    int height = ReadLittleEndianInt16(binaryReader);
                    int width = ReadLittleEndianInt16(binaryReader);
                    return new Size(width, height);
                }
 
                if (chunkLength < 0)
                {
                    ushort uchunkLength = (ushort)chunkLength;
                    binaryReader.ReadBytes(uchunkLength - 2);
                }
                else
                {
                    binaryReader.ReadBytes(chunkLength - 2);
                }
            }
 
            throw new ArgumentException(errorMessage);
        }
    }

Top
#210355 - 2015-06-17 01:20 PM Re: Reading Image Headers to Get Width and Height [Re: MACE]
Allen Administrator Offline
KiX Supporter
*****

Registered: 2003-04-19
Posts: 4545
Loc: USA
No idea if you will run into barriers with the ideas below, but probably the most requested feature we have asked Ruud to add is, have access to .net.

Have you seen these...

Com Interface to Powershell -
http://www.kixtart.org/forums/ubbthreads.php?ubb=showflat&Number=199178#Post199178

Dynamic Wrapper Tutorials -
http://www.kixtart.org/forums/ubbthreads.php?ubb=showflat&Number=189985#Post189985
http://www.kixtart.org/forums/ubbthreads.php?ubb=showflat&Number=190013#Post190013

Using ADO to convert to / from BYTE arrays -
http://www.kixtart.org/forums/ubbthreads.php?ubb=showflat&Number=128970#Post128970

Top
#210356 - 2015-06-17 01:42 PM Re: Reading Image Headers to Get Width and Height [Re: Allen]
Allen Administrator Offline
KiX Supporter
*****

Registered: 2003-04-19
Posts: 4545
Loc: USA
Ohhh.... I forgot about this one... \:\)

GetExtFileProperties -
http://www.kixtart.org/forums/ubbthreads.php?ubb=showflat&Number=160880#Post160880

Dimensions, Frame Hight, Frame Width might work.

Top
#210357 - 2015-06-17 03:38 PM Re: Reading Image Headers to Get Width and Height [Re: Allen]
MACE Offline
Starting to like KiXtart

Registered: 2004-09-07
Posts: 150
Loc: Manchester UK
For the immediate needs, GetExtFileProperties is perfect..
Will look deeper at binary information management later.
ADO may be an option and D.W. looks very promising on several levels.

MANY thanks :-)

 Code:
break on
If instr(@PRODUCTTYPE,'7') or instr(@PRODUCTTYPE,'2008') or instr(@PRODUCTTYPE,'8.1') or instr(@PRODUCTTYPE,'2012')
 Dim $,$Files,$F,$W,$H,$FQFN,$attribute
 $Files=split("testimage.gif,testimage.png,testimage.jpg",',')
 For each $F in $Files
  $FQFN='\\imagepath\'+$F
  ? @crlf+$FQFN
  $attribute='Bit depth'
  ? 'Bit D ='+GetExtFileProperties($FQFN, $attribute)
  $attribute='Horizontal resolution'
  ? 'H DPI ='+GetExtFileProperties($FQFN, $attribute)
  $attribute='Width'
  ? 'Width ='+GetExtFileProperties($FQFN, $attribute)
  $attribute='Vertical resolution'
  ? 'V DPI ='+GetExtFileProperties($FQFN, $attribute)
  $attribute='Height'
  ? 'Height='+GetExtFileProperties($FQFN, $attribute)
 Next
Else
 ? @PRODUCTTYPE+' Not Supported'
EndIf
? @crlf+'Press a key'
get $


function GetExtFileProperties($FQFN, $attribute)
  dim $objShell, $objFolder,$i,$found,$s,$c
  if exist($FQFN)
    $objShell=CreateObject("Shell.Application")
    $objFolder=$objShell.Namespace(left($FQFN,instrrev($FQFN,"\")))
    if $objFolder
      if vartypename($attribute)="string"
        While $i<298 and $found=0
          if $attribute=$objFolder.GetDetailsOf($objFolder.Items, $i)
            $attribute=$i
            $found=1
          endif
          $i=$i+1
        loop
      endif 
      if vartypename($attribute)="long" ; Number  
        $GetExtFileProperties=$objFolder.GetDetailsOf($objFolder.ParseName(right($FQFN,len($FQFN)-instrrev($FQFN,"\"))),$attribute)
      else
        exit -1 
      endif
    else
      exit @error
    endif
  else
    exit 2
  endif
  ; Returns a string with 123 dpi or 234 pixels so strip out to just the numeric values.
  $i=$GetExtFileProperties
  If $i<>''
   $GetExtFileProperties=''
   For $c=1 to len($i)
    $s=asc(substr($i,$c,1))
    If $s >47 and $s <58 or $s=46;Extract numerical values only
     $GetExtFileProperties=$GetExtFileProperties+chr($s)
    EndIf
   Next
  EndIf
endfunction

Top
Page 1 of 1 1


Moderator:  Glenn Barnas, NTDOC, Arend_, Jochen, Radimus, Allen, ShaneEP, Ruud van Velsen, Mart 
Hop to:
Shout Box

Who's Online
0 registered and 507 anonymous users online.
Newest Members
gespanntleuchten, DaveatAdvanced, Paulo_Alves, UsTaaa, xxJJxx
17864 Registered Users

Generated in 0.125 seconds in which 0.085 seconds were spent on a total of 13 queries. Zlib compression enabled.

Search the board with:
superb Board Search
or try with google:
Google
Web kixtart.org