/*
 * $Id: Type3Font.java,v 1.4 2010-05-23 22:07:05 lujke Exp $
 *
 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
package com.sun.pdfview.font;

import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import com.sun.pdfview.PDFFile;
import com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFPage;
import com.sun.pdfview.PDFParser;

/**
 * A Type 3 Font, in which each glyph consists of a sequence of PDF
 * commands.
 * 
 * @author Mike Wessler
 */
public class Type3Font extends PDFFont {

    /** resources for the character definitions */
    HashMap<String,PDFObject> rsrc;
    /** the character processes, mapped by name */
    Map charProcs;
    /** bounding box for the font characters */
    Rectangle2D bbox;
    /** affine transform for the font characters */
    AffineTransform at;
    /** the widths */
    float[] widths;
    /** the start code */
    int firstChar;
    /** the end code */
    int lastChar;

    /**
     * Generate a Type 3 font.
     * @param baseFont the postscript name of this font
     * @param fontObj a dictionary containing references to the character
     * definitions and font information
     * @param resources a set of resources used by the character definitions
     * @param descriptor the descriptor for this font
     */
    public Type3Font(String baseFont, PDFObject fontObj,
            HashMap<String,PDFObject> resources, PDFFontDescriptor descriptor) throws IOException {
        super(baseFont, descriptor);

        rsrc = new HashMap<String, PDFObject>();

        if (resources != null) {
            rsrc.putAll(resources);
        }

        // get the transform matrix
        PDFObject matrix = fontObj.getDictRef("FontMatrix");
        float matrixAry[] = new float[6];
        for (int i = 0; i < 6; i++) {
            matrixAry[i] = matrix.getAt(i).getFloatValue();
        }
        at = new AffineTransform(matrixAry);

        // get the scale from the matrix
        float scale = matrixAry[0] + matrixAry[2];

        // put all the resources in a Hash
        PDFObject rsrcObj = fontObj.getDictRef("Resources");
        if (rsrcObj != null) {
            rsrc.putAll(rsrcObj.getDictionary());
        }

        // get the character processes, indexed by name
        charProcs = fontObj.getDictRef("CharProcs").getDictionary();

        // get the font bounding box
        bbox = PDFFile.parseNormalisedRectangle(fontObj.getDictRef("FontBBox"));
        if (bbox.isEmpty()) {
            bbox = null;
        }

        // get the widths
        PDFObject[] widthArray = fontObj.getDictRef("Widths").getArray();
        widths = new float[widthArray.length];
        for (int i = 0; i < widthArray.length; i++) {
            widths[i] = widthArray[i].getFloatValue();
        }

        // get first and last chars
        firstChar = fontObj.getDictRef("FirstChar").getIntValue();
        lastChar = fontObj.getDictRef("LastChar").getIntValue();
    }

    /**
     * Get the first character code
     */
    public int getFirstChar() {
        return firstChar;
    }

    /**
     * Get the last character code
     */
    public int getLastChar() {
        return lastChar;
    }

    /**
     * Get the glyph for a given character code and name
     *
     * The preferred method of getting the glyph should be by name.  If the
     * name is null or not valid, then the character code should be used.
     * If the both the code and the name are invalid, the undefined glyph 
     * should be returned.
     *
     * Note this method must *always* return a glyph.  
     *
     * @param src the character code of this glyph
     * @param name the name of this glyph or null if unknown
     * @return a glyph for this character
     */
    protected PDFGlyph getGlyph(char src, String name) {
        if (name == null) {
            throw new IllegalArgumentException("Glyph name required for Type3 font!" +
                    "Source character: " + (int) src);
        }

        PDFObject pageObj = (PDFObject) charProcs.get(name);
        if (pageObj == null) {
            // glyph not found.  Return an empty glyph...
            return new PDFGlyph(src, name, new GeneralPath(), new Point2D.Float(0, 0));
        }

        try {
            PDFPage page = new PDFPage(bbox, 0);
            page.addXform(at);

            PDFParser prc = new PDFParser(page, pageObj.getStream(), rsrc);
            prc.go(true);

            float width = widths[src - firstChar];

            Point2D advance = new Point2D.Float(width, 0);
            advance = at.transform(advance, null);

            return new PDFGlyph(src, name, page, advance);
        } catch (IOException ioe) {
            // help!
            System.out.println("IOException in Type3 font: " + ioe);
            ioe.printStackTrace();
            return null;
        }
    }
}

