aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Schweinsberg <[email protected]>2007-02-08 04:30:03 +0000
committerDavid Schweinsberg <[email protected]>2007-02-08 04:30:03 +0000
commitd82d5bc9e90ac2f567049fc6ec5f630ade79ca23 (patch)
treea0015a3411bc36313a845db49861c52c14672d37
parentd7e94b8c4eb48964d158daccf6558d00d4de6d87 (diff)
Added Type 2 Charstring handling to the CFF table.
-rw-r--r--src/net/java/dev/typecast/ot/table/CffTable.java227
-rw-r--r--src/net/java/dev/typecast/ot/table/Charstring.java31
-rw-r--r--src/net/java/dev/typecast/ot/table/CharstringType2.java212
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();
+ }
+}