gdx-studio

#libgdx#java#desktop

git clone https://git.pyrossh.dev/gdx-studio

An IDE for creating Games using libgdx and Java supported on all platforms Android, iOS, Desktop


src_libs/com/badlogic/gdx/tools/hiero/Kerning.java
a62d533 1
/*******************************************************************************
a62d533 2
 * Copyright 2011 See AUTHORS file.
a62d533 3
 * 
a62d533 4
 * Licensed under the Apache License, Version 2.0 (the "License");
a62d533 5
 * you may not use this file except in compliance with the License.
a62d533 6
 * You may obtain a copy of the License at
a62d533 7
 * 
a62d533 8
 *   http://www.apache.org/licenses/LICENSE-2.0
a62d533 9
 * 
a62d533 10
 * Unless required by applicable law or agreed to in writing, software
a62d533 11
 * distributed under the License is distributed on an "AS IS" BASIS,
a62d533 12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
a62d533 13
 * See the License for the specific language governing permissions and
a62d533 14
 * limitations under the License.
a62d533 15
 ******************************************************************************/
a62d533 16
a62d533 17
package com.badlogic.gdx.tools.hiero;
a62d533 18
a62d533 19
import java.awt.font.GlyphVector;
a62d533 20
import java.io.EOFException;
a62d533 21
import java.io.IOException;
a62d533 22
import java.io.InputStream;
a62d533 23
import java.util.ArrayList;
a62d533 24
import java.util.Collections;
a62d533 25
import java.util.HashMap;
a62d533 26
import java.util.Iterator;
a62d533 27
import java.util.List;
a62d533 28
import java.util.ListIterator;
a62d533 29
import java.util.Map;
a62d533 30
import java.util.Map.Entry;
a62d533 31
a62d533 32
/** Reads a TTF font file and provides access to kerning information.
a62d533 33
 * 
a62d533 34
 * Thanks to the Apache FOP project for their inspiring work!
a62d533 35
 * 
a62d533 36
 * @author Nathan Sweet */
a62d533 37
class Kerning {
a62d533 38
	private Map values = Collections.EMPTY_MAP;
a62d533 39
	private int size = -1;
a62d533 40
	private int kerningPairCount = -1;
a62d533 41
	private float scale;
a62d533 42
	private long bytePosition;
a62d533 43
	private long headOffset = -1;
a62d533 44
	private long kernOffset = -1;
a62d533 45
a62d533 46
	/** @param input The data for the TTF font.
a62d533 47
	 * @param size The font size to use to determine kerning pixel offsets.
a62d533 48
	 * @throws IOException If the font could not be read. */
a62d533 49
	public void load (InputStream input, int size) throws IOException {
a62d533 50
		this.size = size;
a62d533 51
		if (input == null) throw new IllegalArgumentException("input cannot be null.");
a62d533 52
		readTableDirectory(input);
a62d533 53
		if (headOffset == -1) throw new IOException("HEAD table not found.");
a62d533 54
		if (kernOffset == -1) {
a62d533 55
			values = Collections.EMPTY_MAP;
a62d533 56
			return;
a62d533 57
		}
a62d533 58
		values = new HashMap(256);
a62d533 59
		if (headOffset < kernOffset) {
a62d533 60
			readHEAD(input);
a62d533 61
			readKERN(input);
a62d533 62
		} else {
a62d533 63
			readKERN(input);
a62d533 64
			readHEAD(input);
a62d533 65
		}
a62d533 66
		input.close();
a62d533 67
a62d533 68
		for (Iterator entryIter = values.entrySet().iterator(); entryIter.hasNext();) {
a62d533 69
			Entry entry = (Entry)entryIter.next();
a62d533 70
			// Scale the offset values using the font size.
a62d533 71
			List valueList = (List)entry.getValue();
a62d533 72
			for (ListIterator valueIter = valueList.listIterator(); valueIter.hasNext();) {
a62d533 73
				int value = ((Integer)valueIter.next()).intValue();
a62d533 74
				int glyphCode = value & 0xffff;
a62d533 75
				int offset = value >> 16;
a62d533 76
				offset = Math.round(offset * scale);
a62d533 77
				if (offset == 0)
a62d533 78
					valueIter.remove();
a62d533 79
				else
a62d533 80
					valueIter.set(new Integer((offset << 16) | glyphCode));
a62d533 81
			}
a62d533 82
			if (valueList.isEmpty()) {
a62d533 83
				entryIter.remove();
a62d533 84
			} else {
a62d533 85
				// Replace ArrayList with int[].
a62d533 86
				int[] valueArray = new int[valueList.size()];
a62d533 87
				int i = 0;
a62d533 88
				for (Iterator valueIter = valueList.iterator(); valueIter.hasNext(); i++)
a62d533 89
					valueArray[i] = ((Integer)valueIter.next()).intValue();
a62d533 90
				entry.setValue(valueArray);
a62d533 91
				kerningPairCount += valueArray.length;
a62d533 92
			}
a62d533 93
		}
a62d533 94
	}
a62d533 95
a62d533 96
	/** Returns the encoded kerning value for the specified glyph. The glyph code for a Unicode codepoint can be retrieved with
a62d533 97
	 * {@link GlyphVector#getGlyphCode(int)}. */
a62d533 98
	public int[] getValues (int firstGlyphCode) {
a62d533 99
		return (int[])values.get(new Integer(firstGlyphCode));
a62d533 100
	}
a62d533 101
a62d533 102
	public int getKerning (int[] values, int otherGlyphCode) {
a62d533 103
		int low = 0;
a62d533 104
		int high = values.length - 1;
a62d533 105
		while (low <= high) {
a62d533 106
			int midIndex = (low + high) >>> 1;
a62d533 107
			int value = values[midIndex];
a62d533 108
			int foundGlyphCode = value & 0xffff;
a62d533 109
			if (foundGlyphCode < otherGlyphCode)
a62d533 110
				low = midIndex + 1;
a62d533 111
			else if (foundGlyphCode > otherGlyphCode)
a62d533 112
				high = midIndex - 1;
a62d533 113
			else
a62d533 114
				return value >> 16;
a62d533 115
		}
a62d533 116
		return 0;
a62d533 117
	}
a62d533 118
a62d533 119
	public int getCount () {
a62d533 120
		return kerningPairCount;
a62d533 121
	}
a62d533 122
a62d533 123
	private void readTableDirectory (InputStream input) throws IOException {
a62d533 124
		skip(input, 4);
a62d533 125
		int tableCount = readUnsignedShort(input);
a62d533 126
		skip(input, 6);
a62d533 127
a62d533 128
		byte[] tagBytes = new byte[4];
a62d533 129
		for (int i = 0; i < tableCount; i++) {
a62d533 130
			tagBytes[0] = readByte(input);
a62d533 131
			tagBytes[1] = readByte(input);
a62d533 132
			tagBytes[2] = readByte(input);
a62d533 133
			tagBytes[3] = readByte(input);
a62d533 134
			skip(input, 4);
a62d533 135
			long offset = readUnsignedLong(input);
a62d533 136
			skip(input, 4);
a62d533 137
a62d533 138
			String tag = new String(tagBytes, "ISO-8859-1");
a62d533 139
			if (tag.equals("head")) {
a62d533 140
				headOffset = offset;
a62d533 141
				if (kernOffset != -1) break;
a62d533 142
			} else if (tag.equals("kern")) {
a62d533 143
				kernOffset = offset;
a62d533 144
				if (headOffset != -1) break;
a62d533 145
			}
a62d533 146
		}
a62d533 147
	}
a62d533 148
a62d533 149
	private void readHEAD (InputStream input) throws IOException {
a62d533 150
		seek(input, headOffset + 2 * 4 + 2 * 4 + 2);
a62d533 151
		int unitsPerEm = readUnsignedShort(input);
a62d533 152
		scale = (float)size / unitsPerEm;
a62d533 153
	}
a62d533 154
a62d533 155
	private void readKERN (InputStream input) throws IOException {
a62d533 156
		seek(input, kernOffset + 2);
a62d533 157
		for (int subTableCount = readUnsignedShort(input); subTableCount > 0; subTableCount--) {
a62d533 158
			skip(input, 2 * 2);
a62d533 159
			int tupleIndex = readUnsignedShort(input);
a62d533 160
			if (!((tupleIndex & 1) != 0) || (tupleIndex & 2) != 0 || (tupleIndex & 4) != 0) return;
a62d533 161
			if (tupleIndex >> 8 != 0) continue;
a62d533 162
a62d533 163
			int kerningCount = readUnsignedShort(input);
a62d533 164
			skip(input, 3 * 2);
a62d533 165
			while (kerningCount-- > 0) {
a62d533 166
				int firstGlyphCode = readUnsignedShort(input);
a62d533 167
				int secondGlyphCode = readUnsignedShort(input);
a62d533 168
				int offset = readShort(input);
a62d533 169
				int value = (offset << 16) | secondGlyphCode;
a62d533 170
a62d533 171
				List firstGlyphValues = (List)values.get(new Integer(firstGlyphCode));
a62d533 172
				if (firstGlyphValues == null) {
a62d533 173
					firstGlyphValues = new ArrayList(256);
a62d533 174
					values.put(new Integer(firstGlyphCode), firstGlyphValues);
a62d533 175
				}
a62d533 176
				firstGlyphValues.add(new Integer(value));
a62d533 177
			}
a62d533 178
		}
a62d533 179
	}
a62d533 180
a62d533 181
	private int readUnsignedByte (InputStream input) throws IOException {
a62d533 182
		bytePosition++;
a62d533 183
		int b = input.read();
a62d533 184
		if (b == -1) throw new EOFException("Unexpected end of file.");
a62d533 185
		return b;
a62d533 186
	}
a62d533 187
a62d533 188
	private byte readByte (InputStream input) throws IOException {
a62d533 189
		return (byte)readUnsignedByte(input);
a62d533 190
	}
a62d533 191
a62d533 192
	private int readUnsignedShort (InputStream input) throws IOException {
a62d533 193
		return (readUnsignedByte(input) << 8) + readUnsignedByte(input);
a62d533 194
	}
a62d533 195
a62d533 196
	private short readShort (InputStream input) throws IOException {
a62d533 197
		return (short)readUnsignedShort(input);
a62d533 198
	}
a62d533 199
a62d533 200
	private long readUnsignedLong (InputStream input) throws IOException {
a62d533 201
		long value = readUnsignedByte(input);
a62d533 202
		value = (value << 8) + readUnsignedByte(input);
a62d533 203
		value = (value << 8) + readUnsignedByte(input);
a62d533 204
		value = (value << 8) + readUnsignedByte(input);
a62d533 205
		return value;
a62d533 206
	}
a62d533 207
a62d533 208
	private void skip (InputStream input, long skip) throws IOException {
a62d533 209
		while (skip > 0) {
a62d533 210
			long skipped = input.skip(skip);
a62d533 211
			if (skipped <= 0) break;
a62d533 212
			bytePosition += skipped;
a62d533 213
			skip -= skipped;
a62d533 214
		}
a62d533 215
	}
a62d533 216
a62d533 217
	private void seek (InputStream input, long position) throws IOException {
a62d533 218
		skip(input, position - bytePosition);
a62d533 219
	}
a62d533 220
}