GlyfCompositeDescript.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;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
/**
* Glyph description for composite glyphs. Composite glyphs are made up of one
* or more simple glyphs, usually with some sort of transformation applied to
* each.
*
* 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 GlyfCompositeDescript extends GlyfDescript
{
/**
* Log instance.
*/
private static final Logger LOG = LogManager.getLogger(GlyfCompositeDescript.class);
private final List<GlyfCompositeComp> components = new ArrayList<>();
private final Map<Integer,GlyphDescription> descriptions = new HashMap<>();
private GlyphTable glyphTable = null;
private boolean beingResolved = false;
private boolean resolved = false;
private int pointCount = -1;
private int contourCount = -1;
/**
* Constructor.
*
* @param bais the stream to be read
* @param glyphTable the Glyphtable containing all glyphs
* @param level current level
* @throws IOException is thrown if something went wrong
*/
GlyfCompositeDescript(TTFDataStream bais, GlyphTable glyphTable, int level) throws IOException
{
super((short) -1);
this.glyphTable = glyphTable;
// Get all of the composite components
GlyfCompositeComp comp;
do
{
comp = new GlyfCompositeComp(bais);
components.add(comp);
}
while ((comp.getFlags() & GlyfCompositeComp.MORE_COMPONENTS) != 0);
// Are there hinting instructions to read?
if ((comp.getFlags() & GlyfCompositeComp.WE_HAVE_INSTRUCTIONS) != 0)
{
readInstructions(bais, (bais.readUnsignedShort()));
}
initDescriptions(level);
}
/**
* {@inheritDoc}
*/
@Override
public void resolve()
{
if (resolved)
{
return;
}
if (beingResolved)
{
LOG.error("Circular reference in GlyfCompositeDesc");
return;
}
beingResolved = true;
int firstIndex = 0;
int firstContour = 0;
for (GlyfCompositeComp comp : components)
{
comp.setFirstIndex(firstIndex);
comp.setFirstContour(firstContour);
GlyphDescription desc = descriptions.get(comp.getGlyphIndex());
if (desc != null)
{
desc.resolve();
firstIndex += desc.getPointCount();
firstContour += desc.getContourCount();
}
}
resolved = true;
beingResolved = false;
}
/**
* {@inheritDoc}
*/
@Override
public int getEndPtOfContours(int i)
{
GlyfCompositeComp c = getCompositeCompEndPt(i);
if (c != null)
{
GlyphDescription gd = descriptions.get(c.getGlyphIndex());
return gd.getEndPtOfContours(i - c.getFirstContour()) + c.getFirstIndex();
}
return 0;
}
/**
* {@inheritDoc}
*/
@Override
public byte getFlags(int i)
{
GlyfCompositeComp c = getCompositeComp(i);
if (c != null)
{
GlyphDescription gd = descriptions.get(c.getGlyphIndex());
return gd.getFlags(i - c.getFirstIndex());
}
return 0;
}
/**
* {@inheritDoc}
*/
@Override
public short getXCoordinate(int i)
{
GlyfCompositeComp c = getCompositeComp(i);
if (c != null)
{
GlyphDescription gd = descriptions.get(c.getGlyphIndex());
int n = i - c.getFirstIndex();
int x = gd.getXCoordinate(n);
int y = gd.getYCoordinate(n);
return (short) (c.scaleX(x, y) + c.getXTranslate());
}
return 0;
}
/**
* {@inheritDoc}
*/
@Override
public short getYCoordinate(int i)
{
GlyfCompositeComp c = getCompositeComp(i);
if (c != null)
{
GlyphDescription gd = descriptions.get(c.getGlyphIndex());
int n = i - c.getFirstIndex();
int x = gd.getXCoordinate(n);
int y = gd.getYCoordinate(n);
return (short) (c.scaleY(x, y) + c.getYTranslate());
}
return 0;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isComposite()
{
return true;
}
/**
* {@inheritDoc}
*/
@Override
public int getPointCount()
{
if (!resolved)
{
LOG.error("getPointCount called on unresolved GlyfCompositeDescript");
}
if (pointCount < 0)
{
GlyfCompositeComp c = components.get(components.size() - 1);
GlyphDescription gd = descriptions.get(c.getGlyphIndex());
if (gd == null)
{
LOG.error("GlyphDescription for index {} is null, returning 0", c.getGlyphIndex());
pointCount = 0;
}
else
{
pointCount = c.getFirstIndex() + gd.getPointCount();
}
}
return pointCount;
}
/**
* {@inheritDoc}
*/
@Override
public int getContourCount()
{
if (!resolved)
{
LOG.error("getContourCount called on unresolved GlyfCompositeDescript");
}
if (contourCount < 0)
{
GlyfCompositeComp c = components.get(components.size() - 1);
GlyphDescription gd = descriptions.get(c.getGlyphIndex());
if (gd == null)
{
LOG.error("missing glyph description for index {}", c.getGlyphIndex());
contourCount = 0;
}
else
{
contourCount = c.getFirstContour() + gd.getContourCount();
}
}
return contourCount;
}
/**
* Get number of components.
*
* @return the number of components
*/
public int getComponentCount()
{
return components.size();
}
/**
* Gets a view to the composite components.
*
* @return unmodifiable list of this composite glyph's {@linkplain GlyfCompositeComp components}
*/
public List<GlyfCompositeComp> getComponents()
{
return Collections.unmodifiableList(components);
}
private GlyfCompositeComp getCompositeComp(int i)
{
for (GlyfCompositeComp c : components)
{
GlyphDescription gd = descriptions.get(c.getGlyphIndex());
if (c.getFirstIndex() <= i && gd != null && i < (c.getFirstIndex() + gd.getPointCount()))
{
return c;
}
}
return null;
}
private GlyfCompositeComp getCompositeCompEndPt(int i)
{
for (GlyfCompositeComp c : components)
{
GlyphDescription gd = descriptions.get(c.getGlyphIndex());
if (c.getFirstContour() <= i && gd != null && i < (c.getFirstContour() + gd.getContourCount()))
{
return c;
}
}
return null;
}
private void initDescriptions(int level)
{
for (GlyfCompositeComp component : components)
{
try
{
int index = component.getGlyphIndex();
GlyphData glyph = glyphTable.getGlyph(index, level);
if (glyph != null)
{
descriptions.put(index, glyph.getDescription());
}
}
catch (IOException e)
{
LOG.error(e);
}
}
}
}