/*
* Copyright (c) 2003-onwards Shaven Puppy Ltd
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'Shaven Puppy' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.shavenpuppy.jglib;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import org.lwjgl.util.Point;
import org.lwjgl.util.Rectangle;
/**
* A Font. Has some global characteristics, and a number of Glyphs, and a mapping of characters to Glyphs.
* A Font can be constructed from various sources.
*/
public final class Font implements Serializable {
private static final long serialVersionUID = 5L;
private static final int MAGIC = 0x4321;
/** Handy bounds */
private static final Rectangle TEMPBOUNDS = new Rectangle();
private static final Point TEMPPOINT = new Point();
/** The font's name */
private String name;
/** The font's style */
private boolean bold, italic;
/** Font's ascent */
private int ascent;
/** Font's descent */
private int descent;
/** Font's leading */
private int leading;
/** Size */
private int size;
/** The font's glyphs */
private Glyph[] glyph;
/** Maps Unicode characters to glyphs */
private int[] map;
/** Lastly an Image of all the glyphs in the font */
private Image image;
/**
* Public constructor for Font for serialization.
*/
public Font() {
}
/**
* Construct a font
*/
public Font(String name, boolean bold, boolean italic, Image image, Glyph[] glyph, int size, int ascent, int descent, int leading, int[] map) {
this.name = name;
this.bold = bold;
this.italic = italic;
this.image = image;
this.size = size;
this.ascent = ascent;
this.descent = descent;
this.leading = leading;
this.map = map;
this.glyph = glyph;
}
public static Font importSerialised(String filename) throws Exception {
InputStream fIn = Font.class.getClassLoader().getResourceAsStream(filename);
BufferedInputStream bIn = new BufferedInputStream(fIn);
Font ret = new Font();
ret.readExternal(bIn);
return ret;
}
public void writeExternal(OutputStream os) throws IOException {
DataOutputStream dos = new DataOutputStream(os);
dos.writeInt(MAGIC);
dos.writeUTF(name);
dos.writeBoolean(bold);
dos.writeBoolean(italic);
dos.writeInt(ascent);
dos.writeInt(descent);
dos.writeInt(leading);
dos.writeInt(size);
dos.writeInt(glyph.length);
for (Glyph g : glyph) {
g.writeExternal(dos);
}
dos.writeInt(map.length);
for (int i : map) {
dos.writeInt(i);
}
image.writeExternal(dos);
}
public void readExternal(InputStream is) throws IOException {
DataInputStream dis = new DataInputStream(is);
int magic = dis.readInt();
if (magic != MAGIC) {
throw new IOException("Expected "+MAGIC+" but got "+magic);
}
name = dis.readUTF();
bold = dis.readBoolean();
italic = dis.readBoolean();
ascent = dis.readInt();
descent = dis.readInt();
leading = dis.readInt();
size = dis.readInt();
glyph = new Glyph[dis.readInt()];
for (int i = 0; i < glyph.length; i ++) {
glyph[i] = new Glyph();
glyph[i].readExternal(dis);
}
map = new int[dis.readInt()];
for (int i = 0; i < map.length; i ++) {
map[i] = dis.readInt();
}
image = new Image();
image.readExternal(dis);
}
/**
* Gets the ascent.
* @return Returns a int
*/
public int getAscent() {
return ascent;
}
/**
* Gets the descent.
* @return Returns a int
*/
public int getDescent() {
return descent;
}
/**
* Gets a glyph.
* @param i The glyph index
* @return a Glyph
*/
public Glyph getGlyph(int i) {
return glyph[i];
}
/**
* @return the number of glyphs
*/
public int getNumGlyphs() {
return glyph.length;
}
/**
* Gets the image.
* @return Returns a SpriteImage
*/
public Image getImage() {
assert image != null : "Font image has been disposed.";
return image;
}
/**
* Gets the leading.
* @return Returns a int
*/
public int getLeading() {
return leading;
}
/**
* Gets the size.
* @return Returns a int
*/
public int getSize() {
return size;
}
/**
* Map a character to a glyph
*/
public Glyph map(char c) {
if (c >= map.length) {
c = 0;
}
if (c == '\t') {
c = ' ';
}
int m = map[c];
assert m != -1 : "char "+((int)c)+" is not valid";
if (m >= glyph.length) {
m = 0;
}
return glyph[m];
}
/**
* Calculate the bounding box of a string if it was drawn at (0,0)
*/
public Rectangle getStringBounds(String text, int start, int end, Rectangle dest) {
if (dest == null) {
dest = new Rectangle();
} else {
dest.setBounds(0,0,0,0);
}
Glyph last = null;
int penX = 0;
for (int i = start; i < end; i ++) {
Glyph next = map(text.charAt(i));
next.getBounds(TEMPBOUNDS);
next.getBearing(TEMPPOINT);
TEMPBOUNDS.setLocation(TEMPPOINT.getX() + penX - next.getKerningAfter(last), TEMPPOINT.getY());
TEMPBOUNDS.setWidth(Math.max(TEMPBOUNDS.getWidth(), next.getAdvance()));
dest.add(TEMPBOUNDS);
penX += next.getAdvance() - next.getKerningAfter(last);
last = next;
}
return dest;
}
/**
* @return the font's name
*/
public String getName() {
return name;
}
/**
* @return true if this is a bold font
*/
public boolean isBold() {
return bold;
}
/**
* @return true if this is an italic font
*/
public boolean isItalic() {
return italic;
}
/**
* @return true if this font is not bold or italic
*/
public boolean isPlain() {
return !(bold || italic);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder buffer = new StringBuilder();
buffer.append("Font [name=");
buffer.append(name);
buffer.append(", bold=");
buffer.append(bold);
buffer.append(", italic=");
buffer.append(italic);
buffer.append(", ascent=");
buffer.append(ascent);
buffer.append(", descent=");
buffer.append(descent);
buffer.append(", leading=");
buffer.append(leading);
buffer.append(", size=");
buffer.append(size);
buffer.append(", image=");
buffer.append(image);
buffer.append("]");
return buffer.toString();
}
}