package org.apache.fop.pdf;

/**
 * Title: PDFXObject
 * Description: render images and SVG by support of Java Advanced Images
 * @author: Hansuli Anderegg, Zurich
 * @version 1.0
 */

import java.io.*;
import org.apache.fop.messaging.MessageHandler;
// JAI
import java.awt.color.*;
import java.awt.color.ColorSpace;
import java.awt.image.*;
import javax.media.jai.*;
import com.sun.media.jai.codec.*;
import javax.media.jai.JAI;
import javax.media.jai.RenderedOp;
import com.sun.media.jai.codec.FileSeekableStream;
// Batik
import org.apache.batik.transcoder.image.JPEGTranscoder;
import org.apache.batik.transcoder.TranscodingHints;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.*;
import org.w3c.dom.*;
import org.w3c.dom.svg.*;
import org.w3c.dom.css.*;
import org.w3c.dom.svg.SVGLength;

import org.apache.fop.render.pdf.PDFRenderer;

/**
 * PDF XObject
 *
 * A derivative of the PDF Object, is a PDF Stream that has not only a
 * dictionary but a stream of image data.
 * the dictionary just provides information like the stream length
 */

public class PDFXObject extends PDFObject {

    int Xnum;
    int width = 0;
    int height = 0;
    int laenge = 0;
    String fn = "";
    String art = "";
    float quality = 0.8F;
    String colorSpace = "";
    Document svg = null;
    byte[] bufferImg;


    // constructor fo:external-graphic image
    public PDFXObject(int number, int Xnumber, String artP, String fnP,
		      String qualityP, String colorSpaceP) {
        super(number);
        this.Xnum = Xnumber;
        this.fn = fnP;
	this.art = artP;
	this.quality = Float.parseFloat(qualityP);
	this.colorSpace = colorSpaceP;
        }
    // constructor fo:instream-foreign-object svg
    public PDFXObject(int number, int Xnumber, String artP, Document svgP, int widthP, int heigthP, String qualityP) {
        super(number);
        this.Xnum = Xnumber;
        this.svg = svgP;
	this.width = widthP;
	this.height = heigthP;
	this.art = artP;
	this.quality = Float.parseFloat(qualityP);
        }

    // constructor external svg
    public PDFXObject(int number, int Xnumber, String artP, String fnP, int widthP, int heigthP, String qualityP) {
        super(number);
        this.Xnum = Xnumber;
        this.fn = fnP;
	this.width = widthP;
	this.height = heigthP;
	this.art = artP;
	this.quality = Float.parseFloat(qualityP);
    }
    /**
     * @return the PDF XObject number
     */
    public int getXNumber() {
        return this.Xnum;
    }

    /**
     * @return the PDF Object number
     */
    public int getNumber() {
        return this.number;
    }

    /**
     * represent as PDF
     */

    protected int output(OutputStream stream) throws IOException {

 String[] colorNames = {"XYZ",
"Lab",
"Luv",
"YCbCr",
"Yxy",
"/DeviceRGB",
"/DeviceGray",
"HSV",
"HLS",
"/DeviceCMYK",
"CMY",
"2 component ",
"3 component ",
"4 component ",
"5 component ",
"6 component ",
"7 component ",
"8 component ",
"9 component ",
"10 component",
"11 component",
"12 component",
"13 component",
"14 component",
"15 component"};

        ByteArrayOutputStream cache = new ByteArrayOutputStream(500000);

if (art.equals("SVG")==true) {

      /*-----------------------------------------------------------*/
      /* have Batik transcode SVG into JPEG                        */
      /*-----------------------------------------------------------*/

      try {
	// create a JPEG transcoder
        JPEGTranscoder t = new JPEGTranscoder();

        // create the transcoder input
        TranscoderInput input = null;
        if (svg==null) { // SVG from external file
	  /*-----------------------------------------------------------*/
			 // Batik would not accept any form of a file name,
			 // so the file is read into memory
	  BufferedReader svgReader = new BufferedReader(
				new InputStreamReader(new FileInputStream(fn)));
	  String svgString = ""; String tmp = "";
	  while (true) {
	    tmp = svgReader.readLine();
	    if (tmp==null) break;
	    svgString = svgString + tmp;
	  }
	  /*-----------------------------------------------------------*/
	  // SVG from external file
 	  BufferedReader is = new BufferedReader(
				new StringReader(svgString));
          input = new TranscoderInput((Reader) is);
        } else {
	  // SVG instream
          input = new TranscoderInput(svg);
        }

        // set the transcoding hints
        t.addTranscodingHint(JPEGTranscoder.KEY_XML_PARSER_CLASSNAME,
                             "org.apache.crimson.parser.XMLReaderImpl");
        t.addTranscodingHint(JPEGTranscoder.KEY_QUALITY,
                             new Float(quality));
	t.addTranscodingHint(JPEGTranscoder.KEY_WIDTH, new Float(width));
	t.addTranscodingHint(JPEGTranscoder.KEY_HEIGHT, new Float(height));

        // create the transcoder output
        TranscoderOutput output = new TranscoderOutput(cache);
        // save the image
        t.transcode(input, output);
        // close the stream then exit
        cache.close();
	bufferImg = cache.toByteArray();
	colorSpace = "/DeviceRGB";
      } catch (Exception e) {MessageHandler.logln("transcoder error: "+e);}

}

else {

    /*-----------------------------------------------------------*/
    /* it's an image                                             */
    /*-----------------------------------------------------------*/
    try {
       FileSeekableStream imgIn = null;
                 imgIn = new FileSeekableStream(fn);

	/*-----------------------------------------------------------*/
	boolean isJPEG = false;           // a JPEG image will be
	byte[]  header = new byte[3];     // copied as is to PDF
	int i = imgIn.read(header, 0, 3);
        if ((header[0] == (byte)0xff) &&
            (header[1] == (byte)0xd8) &&
            (header[2] == (byte)0xff)) {
	  isJPEG = true;
	}
	imgIn.reset();
	/*-----------------------------------------------------------*/

        /* Create an operator to decode the image file.   */
        RenderedOp image1 = JAI.create("stream", imgIn);
        /* The pixel map of the file is read into memory.                  */
        /* The layouter might have to get width and height on the same way */
	/* - Java Image I/O Create reads the file oly partially            */
	width = image1.getWidth();
	height = image1.getHeight();

	ColorModel colorModel = image1.getColorModel();
	ColorSpace cs = colorModel.getColorSpace();
 	if (colorSpace.equals("fromImage")==true) colorSpace = colorNames[cs.getType()];
if (PDFRenderer.getDebug()) System.out.println("\n"+colorModel.toString()
			    +"\ncolor space "+cs.getName(cs.getType())+cs.toString());
	/*-----------------------------------------------------------*/
	/* read JPEG as is into memory                               */
	if (isJPEG==true) {
	  File imgJPEG = new File(fn);
	  DataInputStream jpegIn = new DataInputStream(
				     new BufferedInputStream(
				       new FileInputStream(imgJPEG)));
	  laenge = (int) imgJPEG.length();
	  bufferImg = new byte[laenge];
	  jpegIn.readFully(bufferImg);
	  jpegIn.close();
	/*-----------------------------------------------------------*/
	} else {

	/*-----------------------------------------------------------*/
	/* other image formats are transcoded into JPEG              */
          JPEGEncodeParam encodeParam = new JPEGEncodeParam();

	  // Set the encoding parameters if necessary.
	  encodeParam.setQuality(quality);

	  // Create the encoder.
          ImageEncoder encoder = ImageCodec.createImageEncoder("JPEG",
					    cache,
                                            encodeParam);
	  encoder.encode(image1);
	  cache.close();
	  bufferImg = cache.toByteArray();
	}
      } catch (Exception e) {
               MessageHandler.logln("image error: "+e);
      }
	/*-----------------------------------------------------------*/
}

/*-----------------------------------------------------------*/
/* write the resulting JPEG to a file for diagnosis          */
if (PDFRenderer.getDebug()) {
      try {
	FileOutputStream os01  = new FileOutputStream(fn+".jpg");
	OutputStreamWriter w01 = new OutputStreamWriter(os01);
	BufferedWriter	o01    = new BufferedWriter(w01);
        os01.write(bufferImg);
	os01.close();
      } catch (Exception e) {MessageHandler.logln("diagnose file error: "+e);}
}
/*-----------------------------------------------------------*/

	  /*-----------------------------------------------------------*/
	  /* write the PDF XObject dictionary                          */
	  /*-----------------------------------------------------------*/
	  try {
	    laenge = bufferImg.length;
if (PDFRenderer.getDebug())System.out.println("01 image file/laenge/width/height/colorspace: "
	+fn+" "+laenge+"/"+width+"/"+height+" "+colorSpace);


            String p = number + " " + generation + " obj\n";
            p = p + "<</Type /XObject\n";
            p = p + "/Subtype /Image\n";
            p = p + "/Name /Im" + Xnum + "\n";
            p = p + "/Length " + laenge + "\n";
            p = p + "/Width " +  width + "\n";
            p = p + "/Height " + height + "\n";
            p = p + "/BitsPerComponent 8\n";
            p = p + "/ColorSpace " + colorSpace + "\n";
            p = p + "/Filter /DCTDecode\n";
            p = p + ">>\n";
	    p = p + "stream\n";
	    stream.write(p.getBytes("Cp1252")); // push the pdf dictionary on the writer
	    laenge += p.length();

	    stream.write(bufferImg); // write the JPEG

	    p = "endstream\n";
	    stream.write(p.getBytes("Cp1252"));
	    laenge += p.length();
	    stream.flush();

	  } catch (Exception e) {MessageHandler.logln("XOBject output image error: "+e);}

	  return laenge;
  }


    byte[] toPDF() {
        /*
         * Not used any more
        */
	return null;
    }

}

