diff options
author | David Schweinsberg <[email protected]> | 2007-02-08 04:30:03 +0000 |
---|---|---|
committer | David Schweinsberg <[email protected]> | 2007-02-08 04:30:03 +0000 |
commit | d82d5bc9e90ac2f567049fc6ec5f630ade79ca23 (patch) | |
tree | a0015a3411bc36313a845db49861c52c14672d37 | |
parent | d7e94b8c4eb48964d158daccf6558d00d4de6d87 (diff) |
Added Type 2 Charstring handling to the CFF table.
-rw-r--r-- | src/net/java/dev/typecast/ot/table/CffTable.java | 227 | ||||
-rw-r--r-- | src/net/java/dev/typecast/ot/table/Charstring.java | 31 | ||||
-rw-r--r-- | src/net/java/dev/typecast/ot/table/CharstringType2.java | 212 |
3 files changed, 461 insertions, 9 deletions
diff --git a/src/net/java/dev/typecast/ot/table/CffTable.java b/src/net/java/dev/typecast/ot/table/CffTable.java index fb5e791..74f0454 100644 --- a/src/net/java/dev/typecast/ot/table/CffTable.java +++ b/src/net/java/dev/typecast/ot/table/CffTable.java @@ -1,5 +1,5 @@ /* - * $Id: CffTable.java,v 1.1 2007-02-05 12:42:31 davidsch Exp $ + * $Id: CffTable.java,v 1.2 2007-02-08 04:30:03 davidsch Exp $ * * Typecast - The Font Development Environment * @@ -32,7 +32,7 @@ import java.util.Hashtable; /** * Compact Font Format Table - * @version $Id: CffTable.java,v 1.1 2007-02-05 12:42:31 davidsch Exp $ + * @version $Id: CffTable.java,v 1.2 2007-02-08 04:30:03 davidsch Exp $ * @author <a href="mailto:[email protected]">David Schweinsberg</a> */ public class CffTable implements Table { @@ -263,7 +263,7 @@ public class CffTable implements Table { } } - private class NameIndex extends Index { + public class NameIndex extends Index { protected NameIndex(DataInput di) throws IOException { super(di); @@ -307,6 +307,9 @@ public class CffTable implements Table { return CffStandardStrings.standardStrings[index]; } else { index -= CffStandardStrings.standardStrings.length; + if (index >= getCount()) { + return null; + } int offset = getOffset(index) - 1; int len = getOffset(index + 1) - offset - 1; @@ -329,6 +332,144 @@ public class CffTable implements Table { } } + private class CharsetRange { + + private int _first; + private int _left; + + public int getFirst() { + return _first; + } + + protected void setFirst(int first) { + _first = first; + } + + public int getLeft() { + return _left; + } + + protected void setLeft(int left) { + _left = left; + } + } + + private class CharsetRange1 extends CharsetRange { + + protected CharsetRange1(DataInput di) throws IOException { + setFirst(di.readUnsignedShort()); + setLeft(di.readUnsignedByte()); + } + } + + private class CharsetRange2 extends CharsetRange { + + protected CharsetRange2(DataInput di) throws IOException { + setFirst(di.readUnsignedShort()); + setLeft(di.readUnsignedShort()); + } + } + + private abstract class Charset { + + public abstract int getFormat(); + + public abstract int getSID(int gid); + } + + private class CharsetFormat0 extends Charset { + + private int[] _glyph; + + protected CharsetFormat0(DataInput di, int glyphCount) throws IOException { + _glyph = new int[glyphCount - 1]; // minus 1 because .notdef is omitted + for (int i = 0; i < glyphCount - 1; ++i) { + _glyph[i] = di.readUnsignedShort(); + } + } + + public int getFormat() { + return 0; + } + + public int getSID(int gid) { + if (gid == 0) { + return 0; + } + return _glyph[gid - 1]; + } + } + + private class CharsetFormat1 extends Charset { + + private ArrayList<CharsetRange> _charsetRanges = new ArrayList<CharsetRange>(); + + protected CharsetFormat1(DataInput di, int glyphCount) throws IOException { + int glyphsCovered = glyphCount - 1; // minus 1 because .notdef is omitted + while (glyphsCovered > 0) { + CharsetRange range = new CharsetRange1(di); + _charsetRanges.add(range); + glyphsCovered -= range.getLeft() + 1; + } + } + + public int getFormat() { + return 1; + } + + public int getSID(int gid) { + if (gid == 0) { + return 0; + } + + // Count through the ranges to find the one of interest + int count = 0; + for (CharsetRange range : _charsetRanges) { + count += range.getLeft(); + if (gid < count) { + int sid = gid - count + range.getFirst(); + return sid; + } + } + return 0; + } + } + + private class CharsetFormat2 extends Charset { + + private ArrayList<CharsetRange> _charsetRanges = new ArrayList<CharsetRange>(); + + protected CharsetFormat2(DataInput di, int glyphCount) throws IOException { + int glyphsCovered = glyphCount - 1; // minus 1 because .notdef is omitted + while (glyphsCovered > 0) { + CharsetRange range = new CharsetRange2(di); + _charsetRanges.add(range); + glyphsCovered -= range.getLeft() + 1; + } + } + + public int getFormat() { + return 2; + } + + public int getSID(int gid) { + if (gid == 0) { + return 0; + } + + // Count through the ranges to find the one of interest + int count = 0; + for (CharsetRange range : _charsetRanges) { + if (gid < range.getLeft() + count) { + int sid = gid - count + range.getFirst() - 1; + return sid; + } + count += range.getLeft(); + } + return 0; + } + } + private DirectoryEntry _de; private int _major; private int _minor; @@ -337,6 +478,10 @@ public class CffTable implements Table { private NameIndex _nameIndex; private TopDictIndex _topDictIndex; private StringIndex _stringIndex; + private Index _globalSubrIndex; + private Index _charStringsIndexArray[]; + private Charset[] _charsets; + private Charstring[][] _charstringsArray; private byte[] _buf; @@ -365,13 +510,55 @@ public class CffTable implements Table { // String INDEX _stringIndex = new StringIndex(di2); - // Encodings goes here + // Global Subr INDEX + _globalSubrIndex = new Index(di2); + + // Encodings go here -- but since this is an OpenType font will this + // not always be a CIDFont? In which case there are no encodings + // within the CFF data. + + // Load each of the fonts + _charStringsIndexArray = new Index[_topDictIndex.getCount()]; + _charsets = new Charset[_topDictIndex.getCount()]; + _charstringsArray = new Charstring[_topDictIndex.getCount()][]; + for (int i = 0; i < _topDictIndex.getCount(); ++i) { + + // Charstrings INDEX + // We load this before Charsets because we may need to know the number + // of glyphs + Integer charStringsOffset = (Integer) _topDictIndex.getTopDict(i).getValue(17); + di2 = getDataInputForOffset(charStringsOffset); + _charStringsIndexArray[i] = new Index(di2); + int glyphCount = _charStringsIndexArray[i].getCount(); - // Charsets - Integer charsetOffset = (Integer) _topDictIndex.getTopDict(0).getValue(15); - di2 = getDataInputForOffset(charsetOffset.intValue()); - int format = di2.readUnsignedByte(); - int foo = di2.readUnsignedByte(); + // Charsets + Integer charsetOffset = (Integer) _topDictIndex.getTopDict(i).getValue(15); + di2 = getDataInputForOffset(charsetOffset); + int format = di2.readUnsignedByte(); + switch (format) { + case 0: + _charsets[i] = new CharsetFormat0(di2, glyphCount); + break; + case 1: + _charsets[i] = new CharsetFormat1(di2, glyphCount); + break; + case 2: + _charsets[i] = new CharsetFormat2(di2, glyphCount); + break; + } + + // Create the charstrings + _charstringsArray[i] = new Charstring[glyphCount]; + for (int j = 0; j < glyphCount; ++j) { + int offset = _charStringsIndexArray[i].getOffset(j) - 1; + int len = _charStringsIndexArray[i].getOffset(j + 1) - offset - 1; + _charstringsArray[i][j] = new CharstringType2( + _stringIndex.getString(_charsets[i].getSID(j)), + _charStringsIndexArray[i].getData(), + offset, + len); + } + } } private DataInput getDataInputForOffset(int offset) { @@ -380,6 +567,22 @@ public class CffTable implements Table { _de.getLength() - offset)); } + public NameIndex getNameIndex() { + return _nameIndex; + } + + public Charset getCharset(int fontIndex) { + return _charsets[fontIndex]; + } + + public Charstring getCharstring(int fontIndex, int gid) { + return _charstringsArray[fontIndex][gid]; + } + + public int getCharstringCount(int fontIndex) { + return _charstringsArray[fontIndex].length; + } + public int getType() { return CFF; } @@ -393,6 +596,12 @@ public class CffTable implements Table { sb.append(_topDictIndex.toString()); sb.append("\nString INDEX\n"); sb.append(_stringIndex.toString()); + sb.append("\nGlobal Subr INDEX\n"); + sb.append(_globalSubrIndex.toString()); + for (int i = 0; i < _charStringsIndexArray.length; ++i) { + sb.append("\nCharStrings INDEX ").append(i).append("\n"); + sb.append(_charStringsIndexArray[i].toString()); + } return sb.toString(); } diff --git a/src/net/java/dev/typecast/ot/table/Charstring.java b/src/net/java/dev/typecast/ot/table/Charstring.java new file mode 100644 index 0000000..72f33fe --- /dev/null +++ b/src/net/java/dev/typecast/ot/table/Charstring.java @@ -0,0 +1,31 @@ +/* + * $Id: Charstring.java,v 1.1 2007-02-08 04:30:03 davidsch Exp $ + * + * Typecast - The Font Development Environment + * + * Copyright (c) 2004-2007 David Schweinsberg + * + * Licensed 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 net.java.dev.typecast.ot.table; + +/** + * CFF Charstring + * @version $Id: Charstring.java,v 1.1 2007-02-08 04:30:03 davidsch Exp $ + * @author <a href="mailto:[email protected]">David Schweinsberg</a> + */ +public abstract class Charstring { + + public abstract String getName(); +} diff --git a/src/net/java/dev/typecast/ot/table/CharstringType2.java b/src/net/java/dev/typecast/ot/table/CharstringType2.java new file mode 100644 index 0000000..1e533a9 --- /dev/null +++ b/src/net/java/dev/typecast/ot/table/CharstringType2.java @@ -0,0 +1,212 @@ +/* + * $Id: CharstringType2.java,v 1.1 2007-02-08 04:30:03 davidsch Exp $ + * + * Typecast - The Font Development Environment + * + * Copyright (c) 2004-2007 David Schweinsberg + * + * Licensed 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 net.java.dev.typecast.ot.table; + +/** + * CFF Type 2 Charstring + * @version $Id: CharstringType2.java,v 1.1 2007-02-08 04:30:03 davidsch Exp $ + * @author <a href="mailto:[email protected]">David Schweinsberg</a> + */ +public class CharstringType2 extends Charstring { + + private static final String[] _oneByteOperators = { + "-Reserved-", + "hstem", + "-Reserved-", + "vstem", + "vmoveto", + "rlineto", + "hlineto", + "vlineto", + "rrcurveto", + "-Reserved-", + "callsubr", + "return", + "escape", + "-Reserved-", + "endchar", + "-Reserved-", + "-Reserved-", + "-Reserved-", + "hstemhm", + "hintmask", + "cntrmask", + "rmoveto", + "hmoveto", + "vstemhm", + "rcurveline", + "rlinecurve", + "vvcurveto", + "hhcurveto", + "shortint", + "callgsubr", + "vhcurveto", + "hvcurveto" + }; + + private static final String[] _twoByteOperators = { + "-Reserved- (dotsection)", + "-Reserved-", + "-Reserved-", + "and", + "or", + "not", + "-Reserved-", + "-Reserved-", + "-Reserved-", + "abs", + "add", + "sub", + "div", + "-Reserved-", + "neg", + "eq", + "-Reserved-", + "-Reserved-", + "drop", + "-Reserved-", + "put", + "get", + "ifelse", + "random", + "mul", + "-Reserved-", + "sqrt", + "dup", + "exch", + "index", + "roll", + "-Reserved-", + "-Reserved-", + "-Reserved-", + "hflex", + "flex", + "hflex1", + "flex1", + "-Reserved-" + }; + + private String _name; + private int[] _data; + private int _offset; + private int _length; + private int _index; + + /** Creates a new instance of CharstringType2 */ + protected CharstringType2(String name, int[] data, int offset, int length) { + _name = name; + _data = data; + _offset = offset; + _length = length; + } + + public String getName() { + return _name; + } + + private void disassemble(StringBuffer sb) { + Object operand = null; + while (isOperandAtIndex()) { + operand = nextOperand(); + sb.append(operand).append(" "); + } + int operator = _data[_index++]; + String mnemonic; + if (operator == 12) { + operator = _data[_index++]; + + // Check we're not exceeding the upper limit of our mnemonics + if (operator > 38) { + operator = 38; + } + mnemonic = _twoByteOperators[operator]; + } else { + mnemonic = _oneByteOperators[operator]; + } + sb.append(mnemonic); + } + + private boolean isOperandAtIndex() { + int b0 = _data[_index]; + if ((32 <= b0 && b0 <= 255) || b0 == 28) { + return true; + } + return false; + } + +// private boolean isOperatorAtIndex() { +// int b0 = _data[_index]; +// if ((0 <= b0 && b0 <= 27) || (29 <= b0 && b0 <= 31)) { +// return true; +// } +// return false; +// } + + private Object nextOperand() { + int b0 = _data[_index]; + if (32 <= b0 && b0 <= 246) { + + // 1 byte integer + ++_index; + return new Integer(b0 - 139); + } else if (247 <= b0 && b0 <= 250) { + + // 2 byte integer + int b1 = _data[_index + 1]; + _index += 2; + return new Integer((b0 - 247) * 256 + b1 + 108); + } else if (251 <= b0 && b0 <= 254) { + + // 2 byte integer + int b1 = _data[_index + 1]; + _index += 2; + return new Integer(-(b0 - 251) * 256 - b1 - 108); + } else if (b0 == 28) { + + // 3 byte integer + int b1 = _data[_index + 1]; + int b2 = _data[_index + 2]; + _index += 3; + return new Integer(b1 << 8 | b2); + } else if (b0 == 255) { + + // 16-bit signed integer with 16 bits of fraction + int b1 = (byte) _data[_index + 1]; + int b2 = _data[_index + 2]; + int b3 = _data[_index + 3]; + int b4 = _data[_index + 4]; + _index += 5; + return new Float((b1 << 8 | b2) + ((b3 << 8 | b4) / 65536.0)); + } else { + return null; + } + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + _index = _offset; + while (_index < _offset + _length) { + disassemble(sb); + sb.append("\n"); + } + return sb.toString(); + } +} |