pdfbox-trunk/fontbox/src/main/java/org/apache/fontbox/ttf/GlyfCompositeComp.java
/*

   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */
package org.apache.fontbox.ttf;

import java.io.IOException;

/**
 * This class is based on code from Apache Batik a subproject of Apache XMLGraphics. see
 * http://xmlgraphics.apache.org/batik/ for further details.
 */
public class GlyfCompositeComp
{

    // Flags for composite glyphs.

    /**
     * If set, the arguments are words; otherwise, they are bytes.
     */
    protected static final short ARG_1_AND_2_ARE_WORDS = 0x0001;
    /**
     * If set, the arguments are xy values; otherwise they are points.
     */
    protected static final short ARGS_ARE_XY_VALUES = 0x0002;
    /**
     * If set, xy values are rounded to those of the closest grid lines.
     */
    protected static final short ROUND_XY_TO_GRID = 0x0004;
    /**
     * If set, there is a simple scale; otherwise, scale = 1.0.
     */
    protected static final short WE_HAVE_A_SCALE = 0x0008;
    /**
     * Indicates at least one more glyph after this one.
     */
    protected static final short MORE_COMPONENTS = 0x0020;
    /**
     * The x direction will use a different scale from the y direction.
     */
    protected static final short WE_HAVE_AN_X_AND_Y_SCALE = 0x0040;
    /**
     * There is a 2 by2 transformation that will be used to scale the component.
     */
    protected static final short WE_HAVE_A_TWO_BY_TWO = 0x0080;
    /**
     * Following the last component are instructions for the composite character.
     */
    protected static final short WE_HAVE_INSTRUCTIONS = 0x0100;
    /**
     * If set, this forces the aw and lsb (and rsb) for the composite to be equal to those from this original glyph.
     */
    protected static final short USE_MY_METRICS = 0x0200;

    private int firstIndex;
    private int firstContour;
    private final short argument1;
    private final short argument2;
    private final short flags;
    private final int glyphIndex;
    private double xscale = 1.0;
    private double yscale = 1.0;
    private double scale01 = 0.0;
    private double scale10 = 0.0;
    private int xtranslate = 0;
    private int ytranslate = 0;
    private int point1 = 0;
    private int point2 = 0;

    /**
     * Constructor.
     * 
     * @param bais the stream to be read
     * @throws IOException is thrown if something went wrong
     */
    GlyfCompositeComp(TTFDataStream bais) throws IOException
    {
        flags = bais.readSignedShort();
        glyphIndex = bais.readUnsignedShort();// number of glyph in a font is uint16

        // Get the arguments as just their raw values
        if ((flags & ARG_1_AND_2_ARE_WORDS) != 0)
        {
            // If this is set, the arguments are 16-bit (uint16 or int16)
            argument1 = bais.readSignedShort();
            argument2 = bais.readSignedShort();
        }
        else
        {
            // otherwise, they are bytes (uint8 or int8).
            argument1 = (short) bais.readSignedByte();
            argument2 = (short) bais.readSignedByte();
        }

        // Assign the arguments according to the flags
        if ((flags & ARGS_ARE_XY_VALUES) != 0)
        {
            // If this is set, the arguments are signed xy values
            xtranslate = argument1;
            ytranslate = argument2;
        }
        else
        {
            // otherwise, they are unsigned point numbers.
            //TODO why unused?
            // https://docs.microsoft.com/en-us/typography/opentype/spec/glyf
            // "In the latter case, the first point number indicates the point that is to be matched
            // to the new glyph. The second number indicates the new glyph’s “matched” point.
            // Once a glyph is added, its point numbers begin directly after the last glyphs
            // (endpoint of first glyph + 1).
            point1 = argument1;
            point2 = argument2;
        }

        // Get the scale values (if any)
        if ((flags & WE_HAVE_A_SCALE) != 0)
        {
            int i = bais.readSignedShort();
            xscale = yscale = i / (double) 0x4000;
        }
        else if ((flags & WE_HAVE_AN_X_AND_Y_SCALE) != 0)
        {
            short i = bais.readSignedShort();
            xscale = i / (double) 0x4000;
            i = bais.readSignedShort();
            yscale = i / (double) 0x4000;
        }
        else if ((flags & WE_HAVE_A_TWO_BY_TWO) != 0)
        {
            int i = bais.readSignedShort();
            xscale = i / (double) 0x4000;
            i = bais.readSignedShort();
            scale01 = i / (double) 0x4000;
            i = bais.readSignedShort();
            scale10 = i / (double) 0x4000;
            i = bais.readSignedShort();
            yscale = i / (double) 0x4000;
        }
    }

    /**
     * Sets the first index.
     * 
     * @param idx the first index
     */
    protected void setFirstIndex(int idx)
    {
        firstIndex = idx;
    }

    /**
     * Returns the first index.
     * 
     * @return the first index.
     */
    public int getFirstIndex()
    {
        return firstIndex;
    }

    /**
     * Sets the index for the first contour.
     * 
     * @param idx the index of the first contour
     */
    protected void setFirstContour(int idx)
    {
        firstContour = idx;
    }

    /**
     * Returns the index of the first contour.
     * 
     * @return the index of the first contour.
     */
    public int getFirstContour()
    {
        return firstContour;
    }

    /**
     * Returns argument 1.
     * 
     * @return argument 1.
     */
    public short getArgument1()
    {
        return argument1;
    }

    /**
     * Returns argument 2.
     * 
     * @return argument 2.
     */
    public short getArgument2()
    {
        return argument2;
    }

    /**
     * Returns the flags of the glyph.
     * 
     * @return the flags.
     */
    public short getFlags()
    {
        return flags;
    }

    /**
     * Returns the index of the first contour.
     * 
     * @return index of the first contour.
     */
    public int getGlyphIndex()
    {
        return glyphIndex;
    }

    /**
     * Returns the scale-01 value.
     * 
     * @return the scale-01 value.
     */
    public double getScale01()
    {
        return scale01;
    }

    /**
     * Returns the scale-10 value.
     * 
     * @return the scale-10 value.
     */
    public double getScale10()
    {
        return scale10;
    }

    /**
     * Returns the x-scaling value.
     * 
     * @return the x-scaling value.
     */
    public double getXScale()
    {
        return xscale;
    }

    /**
     * Returns the y-scaling value.
     * 
     * @return the y-scaling value.
     */
    public double getYScale()
    {
        return yscale;
    }

    /**
     * Returns the x-translation value.
     * 
     * @return the x-translation value.
     */
    public int getXTranslate()
    {
        return xtranslate;
    }

    /**
     * Returns the y-translation value.
     * 
     * @return the y-translation value.
     */
    public int getYTranslate()
    {
        return ytranslate;
    }

    /**
     * Transforms an x-coordinate of a point for this component.
     * 
     * @param x The x-coordinate of the point to transform
     * @param y The y-coordinate of the point to transform
     * @return The transformed x-coordinate
     */
    public int scaleX(int x, int y)
    {
        return Math.round((float) (x * xscale + y * scale10));
    }

    /**
     * Transforms a y-coordinate of a point for this component.
     * 
     * @param x The x-coordinate of the point to transform
     * @param y The y-coordinate of the point to transform
     * @return The transformed y-coordinate
     */
    public int scaleY(int x, int y)
    {
        return Math.round((float) (x * scale01 + y * yscale));
    }
}
