GlyfSimpleDescript.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 GlyfSimpleDescript extends GlyfDescript
{
private int[] endPtsOfContours;
private byte[] flags;
private short[] xCoordinates;
private short[] yCoordinates;
private final int pointCount;
/**
* Constructor for an empty description.
*/
GlyfSimpleDescript()
{
super((short) 0);
pointCount = 0;
}
/**
* Constructor.
*
* @param numberOfContours number of contours
* @param bais the stream to be read
* @param x0 the initial X-position
* @throws IOException is thrown if something went wrong
*/
GlyfSimpleDescript(short numberOfContours, TTFDataStream bais, short x0) throws IOException
{
super(numberOfContours);
/*
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html
* "If a glyph has zero contours, it need not have any glyph data." set the pointCount to zero to initialize
* attributes and avoid nullpointer but maybe there shouldn't have GlyphDescript in the GlyphData?
*/
if (numberOfContours == 0)
{
pointCount = 0;
return;
}
// Simple glyph description
endPtsOfContours = bais.readUnsignedShortArray(numberOfContours);
int lastEndPt = endPtsOfContours[numberOfContours - 1];
if (numberOfContours == 1 && lastEndPt == 65535)
{
// PDFBOX-2939: assume an empty glyph
pointCount = 0;
return;
}
// The last end point index reveals the total number of points
pointCount = lastEndPt + 1;
flags = new byte[pointCount];
xCoordinates = new short[pointCount];
yCoordinates = new short[pointCount];
int instructionCount = bais.readUnsignedShort();
readInstructions(bais, instructionCount);
readFlags(pointCount, bais);
readCoords(pointCount, bais, x0);
}
/**
* {@inheritDoc}
*/
@Override
public int getEndPtOfContours(int i)
{
return endPtsOfContours[i];
}
/**
* {@inheritDoc}
*/
@Override
public byte getFlags(int i)
{
return flags[i];
}
/**
* {@inheritDoc}
*/
@Override
public short getXCoordinate(int i)
{
return xCoordinates[i];
}
/**
* {@inheritDoc}
*/
@Override
public short getYCoordinate(int i)
{
return yCoordinates[i];
}
/**
* {@inheritDoc}
*/
@Override
public boolean isComposite()
{
return false;
}
/**
* {@inheritDoc}
*/
@Override
public int getPointCount()
{
return pointCount;
}
/**
* The table is stored as relative values, but we'll store them as absolutes.
*/
private void readCoords(int count, TTFDataStream bais, short x0) throws IOException
{
short x = x0;
short y = 0;
for (int i = 0; i < count; i++)
{
if ((flags[i] & X_DUAL) != 0)
{
if ((flags[i] & X_SHORT_VECTOR) != 0)
{
x += (short) bais.readUnsignedByte();
}
}
else
{
if ((flags[i] & X_SHORT_VECTOR) != 0)
{
x -= (short) bais.readUnsignedByte();
}
else
{
x += bais.readSignedShort();
}
}
xCoordinates[i] = x;
}
for (int i = 0; i < count; i++)
{
if ((flags[i] & Y_DUAL) != 0)
{
if ((flags[i] & Y_SHORT_VECTOR) != 0)
{
y += (short) bais.readUnsignedByte();
}
}
else
{
if ((flags[i] & Y_SHORT_VECTOR) != 0)
{
y -= (short) bais.readUnsignedByte();
}
else
{
y += bais.readSignedShort();
}
}
yCoordinates[i] = y;
}
}
/**
* The flags are run-length encoded.
*/
private void readFlags(int flagCount, TTFDataStream bais) throws IOException
{
for (int index = 0; index < flagCount; index++)
{
flags[index] = (byte) bais.readUnsignedByte();
if ((flags[index] & REPEAT) != 0)
{
int repeats = bais.readUnsignedByte();
for (int i = 1; i <= repeats; i++)
{
if (index + i >= flags.length)
{
throw new IOException(
"repeat count (" + repeats + ") higher than remaining space");
}
flags[index + i] = flags[index];
}
index += repeats;
}
}
}
}