1998.05.01 01:18 "libtiff G4 decompression problems with Kofax images", by Brent Foster

I've got a bunch of files that have been generated using something that identifies itself as Kofax standard Multi-Page TIFF Storage Filter v2.01.000 in the Software tag. libtiff won't decompress them, because the lines get out of sync. This is correct, because Kofax seems to be storing 0 runs at the start of lines and using them as changing pixels.

e.g.
  the ref line is 0,0,1,3,3,19,2...
  the codes are v0,v0,v0,v0,v0,v0,vr1,vl1
  the correct output line is 0,0,1,3,3,19,3
  but in order to get that output line the first two v0 codes must be
  applied to the first two 0 runs, meaning the first pixel changes
colour twice!.
  libtiff (correctly) applies the first v0 to the first 0 run, then
calculates b1 as being 4, because it can't be 0, because 0 is not to
    the right of 0.

libtiff can be modified to handle these images by adding the code below at the start of EXPAND2D. This code basically skips the step of ensuring that b1 is to the right of a0 as long as a0 is zero. It seems to work, and I don't think it'll break any correct G4 images.

Are my thoughts correct? i.e. Are the Kofax G4 images broken, and is libtiff doing correct thing in saying they are? What do we do about it - should I contact Kofax and point this out to them, or is it too late because there are already thousands of their images around, in which case we modify libtiff to handle it? And does someone want some of the images for testing?

Some other (commercial) G4 libraries seem to handle these images ok, but those same ones crash if fed bad G4 data, so I would assume that they aren't correctly checking b1 and they're just assuming that (when appropriate) adding the next run to b1 will make b1>a0, unlike what libtiff does. I guess not checking properly would mean zero runs in the middle of a line would cause all sorts of havoc.

Hope this makes sense, and thanks for any help.

Brent

while (a0 == 0) {
  NeedBits8(7, eof2d);
  TabEnt = TIFFFaxMainTable + GetBits(7);
  switch (TabEnt->State) {
  case S_Pass :
    ClrBits(TabEnt->Width);
    b1 += *pb++;
    RunLength += b1 - a0;
    a0 = b1;
    b1 += *pb++;
    break;
  case S_V0 :
    ClrBits(TabEnt->Width);
    SETVAL(b1 - a0);
    b1 += *pb++;
    break;
  case S_VR :
    ClrBits(TabEnt->Width);
    SETVAL(b1 - a0 + TabEnt->Param);
    b1 += *pb++;
    break;
  default :
    goto start2d;
  }
}
start2d :