Need help to start converting original G3 fax files to TIFF format via Java
I have an original fax file (G3 / T.4 format), which needs to be programmatically converted into multi page TIFF through Java So far, Jai has not been successful, even though I think it should be effective The tool from sfaxtools has successfully converted my original fax file to TIFF (batch fax2tif or faxsee), but I need to do this programmatically through Java I think it should be possible to use Java advanced imaging. Please check the following code snippet:
private void writeTiff(byte[] buffer,OutputStream outStream) { try { //reading image from given buffer RenderedImage rendImage = null; TIFFDecodeParam decodeParams = new TIFFDecodeparam(); ByteArrayInputStream stream = new ByteArrayInputStream(buffer); ImageDecoder decoder = ImageCodec.createImageDecoder("tiff",stream,decodeParams); TIFFEncodeParam encodeParams = new TIFFEncodeparam(); int numPages = decoder.getNumPages(); for (int i = 0; i < numPages; i++) { rendImage = decoder.decodeAsRenderedImage(i); ImageEncoder encoder = ImageCodec.createImageEncoder("TIFF",outStream,encodeParams); encoder.encode(rendImage); } } catch (Exception e) { e.printStackTrace(); } catch (Error err) { err.printStackTrace(); } }
The problem is, especially in the reading section, imagedecoder decoder = imagecodec createImageDecoder(“tiff”,decodeParams); It should be replaced by some imagedecoder implementations that internally use faxdecoder to decode G3 original fax files There is a protected class tifffaxdecoder in the Jai package. Is it possible and how to use it for my purposes? Any ideas? thank you
Solution
I don't think Jai supports direct reading of G3 / T.4 original fax data However, the sample code here can be modified and extended to meet your needs and implement the ideas outlined in the review (originally published as GIST)
It does not decode G3 / T.4 data in any way, it just packages the original fax data in the smallest TIFF container This allows the data to be read later as normal tiff It is implemented using the (my own) twelvemonkeys imageio library
If you don't know the width / height of fax files, you can find them by using ccittfaxdecoderstream, trying to implement the algorithm with different widths (columns) defined in the standard, and see how many whole lines you can read If you have the correct number, you should consume the stream completely
import com.twelvemonkeys.imageio.Metadata.AbstractEntry; import com.twelvemonkeys.imageio.Metadata.Entry; import com.twelvemonkeys.imageio.Metadata.exif.EXIFWriter; import com.twelvemonkeys.imageio.Metadata.exif.Rational; import com.twelvemonkeys.imageio.Metadata.exif.TIFF; import javax.imageio.ImageIO; import javax.imageio.stream.ImageOutputStream; import java.io.*; import java.util.ArrayList; public class G3Test { public static void main(String[] args) throws IOException { File input = new File(args[0]); File output = new File(args.length > 1 ? args[1] : input.getName().replace(".g3",".tif")); // ImageWidth = 1728,2048,2482. SHORT or LONG. These are the fixed page widths in pixels defined in CCITT Group 3. int columns = 1728; // The default int rows = 100; // Trial and error for sample file found at http://www.filesuffix.com/en/extension/fax ArrayList<Entry> entries = new ArrayList<>(); // http://cool.conservation-us.org/bytopic/imaging/std/tiff-f.html // required Class F tags entries.add(new TIFFEntry(TIFF.TAG_COMPRESSION,TIFF.TYPE_SHORT,3)); // CCITT T.4 entries.add(new TIFFEntry(TIFF.TAG_FILL_ORDER,1)); // Left to right entries.add(new TIFFEntry(TIFF.TAG_GROUP3OPTIONS,TIFF.TYPE_LONG,0)); // No options set entries.add(new TIFFEntry(TIFF.TAG_IMAGE_WIDTH,columns)); entries.add(new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT,rows)); entries.add(new TIFFEntry(TIFF.TAG_SUBFILE_TYPE,2)); // Page entries.add(new TIFFEntry(TIFF.TAG_RESOLUTION_UNIT,2)); // Inches entries.add(new TIFFEntry(TIFF.TAG_X_RESOLUTION,TIFF.TYPE_RATIONAL,new Rational(204))); // 204 entries.add(new TIFFEntry(TIFF.TAG_Y_RESOLUTION,new Rational(98))); // 98,196 // required Bilevel (Class B) tags entries.add(new TIFFEntry(TIFF.TAG_BITS_PER_SAMPLE,1)); // 1 bit/sample entries.add(new TIFFEntry(TIFF.TAG_PHOTOMETRIC_INTERPRETATION,0)); // White is zero entries.add(new TIFFEntry(TIFF.TAG_SOFTWARE,TIFF.TYPE_ASCII,"TwelveMonkeys FAX2TIFF 0.1 BETA ;-)")); entries.add(new TIFFEntry(TIFF.TAG_ROWS_PER_STRIP,rows)); entries.add(new TIFFEntry(TIFF.TAG_SAMPLES_PER_PIXEL,1)); // 1 sample/pixel entries.add(new TIFFEntry(TIFF.TAG_STRIP_BYTE_COUNTS,input.length())); entries.add(new TIFFEntry(TIFF.TAG_STRIP_OFFSETS,-1)); // placeholder for Now // We Now have all our entries,compute size of the entries,and make that the offset (we'll write the data right after). EXIFWriter writer = new EXIFWriter(); long offset = 12 + writer.computeIFDSize(entries); // + 12 for TIFF magic (4),IFD0 pointer (4) and EOF (4) entries.remove(entries.size() - 1); entries.add(new TIFFEntry(TIFF.TAG_STRIP_OFFSETS,offset)); try (InputStream in = new FileInputStream(input)) { try (ImageOutputStream out = ImageIO.createImageOutputStream(output)) { // Write the TIFF IFD for the image data writer.write(entries,out); // Copy the already G3 compressed bytes verbatim to the output byte[] buffer = new byte[1024]; int read; while ((read = in.read(buffer)) >= 0) { out.write(buffer,read); } } } } // API stupidity,should be fixed in later verisons (ie. contain a predefined TIFFEntry class) static final class TIFFEntry extends AbstractEntry { private final short type; TIFFEntry(int identifier,short type,Object value) { super(identifier,value); this.type = type; } @Override public String getTypeName() { return TIFF.TYPE_NAMES[type]; } } }