/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.awt.font.truetype.glyph; import java.awt.geom.GeneralPath; import java.io.IOException; import org.jnode.awt.font.truetype.TTFInput; public class SimpleGlyph extends TTFGlyph { private static final int ON_CURVE = 0; private static final int X_SHORT = 1; private static final int Y_SHORT = 2; private static final int REPEAT_FLAG = 3; private static final int X_SAME = 4; private static final int Y_SAME = 5; private static final int X_POSITIVE = 4; private static final int Y_POSITIVE = 5; private int numberOfContours; private int[] endPtsOfContours; private int[] instructions; private int[] flags; private int[] xCoordinates, yCoordinates; private boolean[] onCurve; private GeneralPath shape; public SimpleGlyph(TTFInput in, int numberOfContours) throws IOException { super(in); this.numberOfContours = numberOfContours; this.endPtsOfContours = new int[numberOfContours]; read(in); } public String getType() { return "Simple Glyph"; } private void read(TTFInput ttf) throws IOException { // read Array of last points of each contour for (int i = 0; i < endPtsOfContours.length; i++) { endPtsOfContours[i] = ttf.readUShort(); } // read the number of instructions and allocate memory for them instructions = new int[ttf.readUShort()]; // read all the instructions for (int i = 0; i < instructions.length; i++) { instructions[i] = ttf.readByte(); } int numberOfPoints = endPtsOfContours[endPtsOfContours.length - 1] + 1; // allocate memory for flags, xCoordinates[], yCoordinates[] flags = new int[numberOfPoints]; xCoordinates = new int[numberOfPoints]; yCoordinates = new int[numberOfPoints]; onCurve = new boolean[numberOfPoints]; // if should be repeted than return allways the last byte of // repeatCountTimes int repeatCount = 0; int repeatFlag = 0; // read the flags for (int i = 0; i < numberOfPoints; i++) { // if repeatCount was seted than return the same byte and // decrementthe number of repeats if (repeatCount > 0) { flags[i] = repeatFlag; repeatCount--; } else { // read the flag flags[i] = ttf.readRawByte(); // if repeat is seted than read how many times if (TTFInput.flagBit(flags[i], REPEAT_FLAG)) { repeatCount = ttf.readByte(); repeatFlag = flags[i]; } } TTFInput.checkZeroBit(flags[i], 6, "flags"); TTFInput.checkZeroBit(flags[i], 7, "flags"); onCurve[i] = TTFInput.flagBit(flags[i], ON_CURVE); } int last = 0; // read xCoordinates for (int i = 0; i < numberOfPoints; i++) { if (TTFInput.flagBit(flags[i], X_SHORT)) { if (TTFInput.flagBit(flags[i], X_POSITIVE)) { last = xCoordinates[i] = last + ttf.readByte(); } else { last = xCoordinates[i] = last - ttf.readByte(); } } else { if (TTFInput.flagBit(flags[i], X_SAME)) { last = xCoordinates[i] = last; } else { last = xCoordinates[i] = last + ttf.readShort(); } } } last = 0; // read yCoordinates for (int i = 0; i < numberOfPoints; i++) { if (TTFInput.flagBit(flags[i], Y_SHORT)) { if (TTFInput.flagBit(flags[i], Y_POSITIVE)) { last = yCoordinates[i] = last + ttf.readByte(); } else { last = yCoordinates[i] = last - ttf.readByte(); } } else { if (TTFInput.flagBit(flags[i], Y_SAME)) { last = yCoordinates[i] = last; } else { last = yCoordinates[i] = last + ttf.readShort(); } } } } public String toString() { String str = super.toString() + ", " + numberOfContours + " contours, endPts={"; for (int i = 0; i < numberOfContours; i++) str += (i == 0 ? "" : ",") + endPtsOfContours[i]; str += "}, " + instructions.length + " instructions"; return str; } public String toDetailedString() { String str = toString() + "\n instructions = {"; for (int i = 0; i < instructions.length; i++) { str += Integer.toHexString(instructions[i]) + " "; } return str + "}"; } public GeneralPath getShape() { if (shape != null) { return shape; } shape = new GeneralPath(GeneralPath.WIND_NON_ZERO); int p = 0; int x = 0; int y = 0; for (int i = 0; i < endPtsOfContours.length; i++) { boolean first = true; while (p <= endPtsOfContours[i]) { x = xCoordinates[p]; y = yCoordinates[p]; //System.out.print(p+": ("+x+","+y+")"); if (first) { shape.moveTo(x, y); //System.out.println(" m"); if (!onCurve[p]) { //System.err.println("First point of contour not on curve!"); } else { //TODO fix it, see bellow } } else if (onCurve[p]) { shape.lineTo(x, y); //System.out.println(" l"); } else { int pIndex = 0; // when we are at the end of a contour if (p == endPtsOfContours[i]) // look for the endpoint of the curve at the beginning if (i > 0) pIndex = endPtsOfContours[i - 1] + 1; else pIndex = 0; else pIndex = ++p; // else take the next point int x1 = xCoordinates[pIndex]; int y1 = yCoordinates[pIndex]; //System.out.print("("+x1+","+y1+")"); if (onCurve[p]) { shape.quadTo(x, y, x1, y1); //System.out.println(" q"); } else { if (p == endPtsOfContours[i]) if (i > 0) pIndex = endPtsOfContours[i - 1] + 1; else pIndex = 0; else pIndex = ++p; int x2 = xCoordinates[pIndex]; int y2 = yCoordinates[pIndex]; //System.out.println("("+y2+","+y2+") c"); shape.curveTo(x, y, x1, y1, x2, y2); // FIXME: Find out how to construct a cubic or quadratic // Bezier curve from a Bezier spline with an arbitrary number // of off-curve points if (!onCurve[p]) { //System.err.println("Three points in a row not on curve!"); } else { //TODO see above } } } first = false; p++; } shape.closePath(); //System.out.println("."); } return shape; } }