SWF archeology: how to retrieve the dimensions from a flash (swf) file

Embedding flash objects can be troublesome, if you don’t know the native resolution of the file you want to display.

Of course you can use the embed tag or the object tag and set its width and height to 100%, but you have to stop editing and view your page to find out how big the object actually is.

In a WYSIWYG html richtext editor a placeholder that has exactly the size of the swf object helps a great deal to get an impression how big the element will actually be when viewing it.

Let’s see how this can be accomplished:
swfs files store the width and height of the object in the file’s header.
All we need to do is read the file header and interpret our findings.

The first 3 bytes of a swf file indicate the file type.
FWS is the magic byte number for normal swf, CWS indicates a compressed file, where everything beyond byte 8 is zlib compressed. We can also find version info and file size in the header after byte 3, but this has nothing to do with the file’s dimensions.

Reading another 9 bytes starting with byte 9 (this is where the header ends) will give us a rect structure, which stores the file’s dimensions. Read it in high-to-low order.

If you don’t know what a rect structure is (I didn’t know either), fortunately things are well documented.

structure of rect
Nbits nBits = UB[5]
Bits in each rect value field
Xmin SB[nBits] X minimum position for rect
Xmax SB[nBits] X maximum position for rect
Ymin SB[nBits] Y minimum position for rect
Ymax SB[nBits] Y maximum position for rect
Xmax-Xmin will give us the dimensions for x. The length of the Xmax and Xmin field in bits is determined by nbits.

Still, the results appear off the scale. Nothing wrong here, x and y dimensions are stored in twips (some invention of ressourceful typesetters?). Just divide the results by 20 and you get the size in pixel.


Here is a simple example in tcl (if you are looking for examples in c, there’s an overabundance of them on the web):

set f [open $swffile r]
fconfigure $f -translation binary -encoding binary
set header [read $f 3]
set version [read $f 1]
#size is uint32
set size [read $f 4]
set data [read $f]
close $f

if {$header ni {FWS CWS}} {
  #file is not flash
  return
}

#if the file is compressed, uncompress it first
if {$header eq {CWS}} {
  #compressed file, uncompress with zlib
  #we could use something nicer here, like ns_zlib or the zlib package
  set fileID [open "/tmp/swfuncompressed_XXXXXX" w]
  fconfigure $fileID -translation binary -encoding binary
  puts $fileID $data
  close $fileID
  set data [eval exec "/usr/bin/openssl zlib -d -in $tmp_file"]
  file delete $tmp_file
}

#rect contains dimensions
set rect [string range $data 0 9]

#structure of rect
#Nbits nBits = UB[5]
#Bits in each rect value field
#Xmin SB[nBits] X minimum position for rect
#Xmax SB[nBits] X maximum position for rect
#Ymin SB[nBits] Y minimum position for rect
#Ymax SB[nBits] Y maximum position for rect
binary scan $rect B* bin_rect
set end 4
set start 0
set nbit 0
foreach char [split [string range $bin_rect $start $end] ""] {
  set nbit [expr {($nbit << 1) + $char}]
}

for {set i 0} {$i<4} {incr i} {
  set start [expr {$end+1}]
  set end [expr {$end+$nbit}]
  switch $i {
    0 {
      set xmin 0
      foreach char [split [string range $bin_rect $start $end] ""] {
        set xmin [expr {($xmin << 1) + $char}]
      }
    }
    1 {
      set xmax 0
      foreach char [split [string range $bin_rect $start $end] ""] {
        set xmax [expr {($xmax << 1) + $char}]
      }
    }
    2 {
      set ymin 0
      foreach char [split [string range $bin_rect $start $end] ""] {
        set ymin [expr {($ymin << 1) + $char}]
      }
    }
    3 {
      set ymax 0
      foreach char [split [string range $bin_rect $start $end] ""] {
        set ymax [expr {($ymax << 1) + $char}]
      }
   }
  }
}

set dimensions(width) [expr {($xmax-$xmin)/20}]
set dimensions(height) [expr {($ymax-$ymin)/20}]
return [array get dimensions]

 

Advertisements