From 0c64891ea399a9c5fd95753c549c604e46a7bc23 Mon Sep 17 00:00:00 2001 From: David Schweinsberg Date: Tue, 29 Dec 2015 23:29:26 -0800 Subject: Moved CffTable inner classes out into CFF-specific package. --- .../java/dev/typecast/app/editor/GlyphPanel.java | 2 +- src/net/java/dev/typecast/app/editor/Main.java | 2 +- .../dev/typecast/app/editor/TableTreeBuilder.java | 5 +- src/net/java/dev/typecast/ot/Glyph.java | 9 +- .../dev/typecast/ot/table/CffStandardStrings.java | 421 ----------------- src/net/java/dev/typecast/ot/table/CffTable.java | 506 +-------------------- src/net/java/dev/typecast/ot/table/Charstring.java | 30 -- .../dev/typecast/ot/table/CharstringType2.java | 228 ---------- src/net/java/dev/typecast/t2/CffFont.java | 64 +++ .../java/dev/typecast/t2/CffStandardStrings.java | 421 +++++++++++++++++ src/net/java/dev/typecast/t2/Charset.java | 33 ++ src/net/java/dev/typecast/t2/CharsetFormat0.java | 51 +++ src/net/java/dev/typecast/t2/CharsetFormat1.java | 63 +++ src/net/java/dev/typecast/t2/CharsetFormat2.java | 63 +++ src/net/java/dev/typecast/t2/CharsetRange.java | 48 ++ src/net/java/dev/typecast/t2/CharsetRange1.java | 34 ++ src/net/java/dev/typecast/t2/CharsetRange2.java | 34 ++ src/net/java/dev/typecast/t2/Charstring.java | 30 ++ src/net/java/dev/typecast/t2/CharstringType2.java | 228 ++++++++++ src/net/java/dev/typecast/t2/Dict.java | 169 +++++++ src/net/java/dev/typecast/t2/Index.java | 89 ++++ src/net/java/dev/typecast/t2/NameIndex.java | 57 +++ src/net/java/dev/typecast/t2/StringIndex.java | 62 +++ src/net/java/dev/typecast/t2/T2Interpreter.java | 8 +- src/net/java/dev/typecast/t2/TopDictIndex.java | 48 ++ 25 files changed, 1519 insertions(+), 1186 deletions(-) delete mode 100644 src/net/java/dev/typecast/ot/table/CffStandardStrings.java delete mode 100644 src/net/java/dev/typecast/ot/table/Charstring.java delete mode 100644 src/net/java/dev/typecast/ot/table/CharstringType2.java create mode 100644 src/net/java/dev/typecast/t2/CffFont.java create mode 100644 src/net/java/dev/typecast/t2/CffStandardStrings.java create mode 100644 src/net/java/dev/typecast/t2/Charset.java create mode 100644 src/net/java/dev/typecast/t2/CharsetFormat0.java create mode 100644 src/net/java/dev/typecast/t2/CharsetFormat1.java create mode 100644 src/net/java/dev/typecast/t2/CharsetFormat2.java create mode 100644 src/net/java/dev/typecast/t2/CharsetRange.java create mode 100644 src/net/java/dev/typecast/t2/CharsetRange1.java create mode 100644 src/net/java/dev/typecast/t2/CharsetRange2.java create mode 100644 src/net/java/dev/typecast/t2/Charstring.java create mode 100644 src/net/java/dev/typecast/t2/CharstringType2.java create mode 100644 src/net/java/dev/typecast/t2/Dict.java create mode 100644 src/net/java/dev/typecast/t2/Index.java create mode 100644 src/net/java/dev/typecast/t2/NameIndex.java create mode 100644 src/net/java/dev/typecast/t2/StringIndex.java create mode 100644 src/net/java/dev/typecast/t2/TopDictIndex.java (limited to 'src') diff --git a/src/net/java/dev/typecast/app/editor/GlyphPanel.java b/src/net/java/dev/typecast/app/editor/GlyphPanel.java index 977d351..a992261 100644 --- a/src/net/java/dev/typecast/app/editor/GlyphPanel.java +++ b/src/net/java/dev/typecast/app/editor/GlyphPanel.java @@ -26,7 +26,7 @@ import net.java.dev.typecast.app.framework.EditorView; import net.java.dev.typecast.edit.GlyphEdit; import net.java.dev.typecast.ot.Glyph; import net.java.dev.typecast.ot.OTFont; -import net.java.dev.typecast.ot.table.Charstring; +import net.java.dev.typecast.t2.Charstring; import net.java.dev.typecast.ot.table.GlyphDescription; /** diff --git a/src/net/java/dev/typecast/app/editor/Main.java b/src/net/java/dev/typecast/app/editor/Main.java index a427e3b..034b191 100644 --- a/src/net/java/dev/typecast/app/editor/Main.java +++ b/src/net/java/dev/typecast/app/editor/Main.java @@ -388,7 +388,7 @@ public class Main { // Then add the panes we're interested in if (obj instanceof GlyphDescription - || obj instanceof net.java.dev.typecast.ot.table.Charstring) { + || obj instanceof net.java.dev.typecast.t2.Charstring) { _glyphPane = new GlyphPanel(_appPrefs); _glyphPane.setModel(font, obj); _tabbedPane.add(_glyphPane); diff --git a/src/net/java/dev/typecast/app/editor/TableTreeBuilder.java b/src/net/java/dev/typecast/app/editor/TableTreeBuilder.java index eacd719..39ad633 100644 --- a/src/net/java/dev/typecast/app/editor/TableTreeBuilder.java +++ b/src/net/java/dev/typecast/app/editor/TableTreeBuilder.java @@ -37,12 +37,13 @@ import net.java.dev.typecast.ot.table.GlyfCompositeDescript; import net.java.dev.typecast.ot.table.GlyfDescript; import net.java.dev.typecast.ot.table.GlyfTable; import net.java.dev.typecast.ot.table.CffTable; -import net.java.dev.typecast.ot.table.Charstring; +import net.java.dev.typecast.t2.Charstring; import net.java.dev.typecast.ot.table.GsubTable; import net.java.dev.typecast.ot.table.ID; import net.java.dev.typecast.ot.table.LangSys; import net.java.dev.typecast.ot.table.Lookup; import net.java.dev.typecast.ot.table.LookupSubtable; +import net.java.dev.typecast.t2.NameIndex; import net.java.dev.typecast.ot.table.NameRecord; import net.java.dev.typecast.ot.table.NameTable; import net.java.dev.typecast.ot.table.PostTable; @@ -241,7 +242,7 @@ public class TableTreeBuilder { } private static void addCffTable(OTFont font, TableTreeNode parent, CffTable ct) { - CffTable.NameIndex ni = ct.getNameIndex(); + NameIndex ni = ct.getNameIndex(); for (int i = 0; i < ni.getCount(); ++i) { TableTreeNode n = new TableTreeNode( ni.getName(i), diff --git a/src/net/java/dev/typecast/ot/Glyph.java b/src/net/java/dev/typecast/ot/Glyph.java index 2cd36bd..8dfa015 100644 --- a/src/net/java/dev/typecast/ot/Glyph.java +++ b/src/net/java/dev/typecast/ot/Glyph.java @@ -53,8 +53,9 @@ package net.java.dev.typecast.ot; import net.java.dev.typecast.ot.table.CffTable; import net.java.dev.typecast.ot.table.GlyphDescription; import net.java.dev.typecast.ot.table.GlyfDescript; -import net.java.dev.typecast.ot.table.Charstring; -import net.java.dev.typecast.ot.table.CharstringType2; +import net.java.dev.typecast.t2.Charstring; +import net.java.dev.typecast.t2.CharstringType2; +import net.java.dev.typecast.t2.Index; import net.java.dev.typecast.t2.T2Interpreter; @@ -93,8 +94,8 @@ public class Glyph { Charstring cs, short lsb, int advance, - CffTable.Index localSubrIndex, - CffTable.Index globalSubrIndex) { + Index localSubrIndex, + Index globalSubrIndex) { _leftSideBearing = lsb; _advanceWidth = advance; if (cs instanceof CharstringType2) { diff --git a/src/net/java/dev/typecast/ot/table/CffStandardStrings.java b/src/net/java/dev/typecast/ot/table/CffStandardStrings.java deleted file mode 100644 index 326f115..0000000 --- a/src/net/java/dev/typecast/ot/table/CffStandardStrings.java +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Typecast - The Font Development Environment - * - * Copyright (c) 2004-2015 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; - -/** - * Compact Font Format Standard Strings. As per Appendix A of the Adobe - * CFF specification. - * @author David Schweinsberg - */ -public class CffStandardStrings { - - public static final String[] standardStrings = { - ".notdef", - "space", - "exclam", - "quotedbl", - "numbersign", - "dollar", - "percent", - "ampersand", - "quoteright", - "parenleft", - "parenright", - "asterisk", - "plus", - "comma", - "hyphen", - "period", - "slash", - "zero", - "one", - "two", - "three", - "four", - "five", - "six", - "seven", - "eight", - "nine", - "colon", - "semicolon", - "less", - "equal", - "greater", - "question", - "at", - "A", - "B", - "C", - "D", - "E", - "F", - "G", - "H", - "I", - "J", - "K", - "L", - "M", - "N", - "O", - "P", - "Q", - "R", - "S", - "T", - "U", - "V", - "W", - "X", - "Y", - "Z", - "bracketleft", - "backslash", - "bracketright", - "asciicircum", - "underscore", - "quoteleft", - "a", - "b", - "c", - "d", - "e", - "f", - "g", - "h", - "i", - "j", - "k", - "l", - "m", - "n", - "o", - "p", - "q", - "r", - "s", - "t", - "u", - "v", - "w", - "x", - "y", - "z", - "braceleft", - "bar", - "braceright", - "asciitilde", - "exclamdown", - "cent", - "sterling", - "fraction", - "yen", - "florin", - "section", - "currency", - "quotesingle", - "quotedblleft", - "guillemotleft", - "guilsinglleft", - "guilsinglright", - "fi", - "fl", - "endash", - "dagger", - "daggerdbl", - "periodcentered", - "paragraph", - "bullet", - "quotesinglbase", - "quotedblbase", - "quotedblright", - "guillemotright", - "ellipsis", - "perthousand", - "questiondown", - "grave", - "acute", - "circumflex", - "tilde", - "macron", - "breve", - "dotaccent", - "dieresis", - "ring", - "cedilla", - "hungarumlaut", - "ogonek", - "caron", - "emdash", - "AE", - "ordfeminine", - "Lslash", - "Oslash", - "OE", - "ordmasculine", - "ae", - "dotlessi", - "lslash", - "oslash", - "oe", - "germandbls", - "onesuperior", - "logicalnot", - "mu", - "trademark", - "Eth", - "onehalf", - "plusminus", - "Thorn", - "onequarter", - "divide", - "brokenbar", - "degree", - "thorn", - "threequarters", - "twosuperior", - "registered", - "minus", - "eth", - "multiply", - "threesuperior", - "copyright", - "Aacute", - "Acircumflex", - "Adieresis", - "Agrave", - "Aring", - "Atilde", - "Ccedilla", - "Eacute", - "Ecircumflex", - "Edieresis", - "Egrave", - "Iacute", - "Icircumflex", - "Idieresis", - "Igrave", - "Ntilde", - "Oacute", - "Ocircumflex", - "Odieresis", - "Ograve", - "Otilde", - "Scaron", - "Uacute", - "Ucircumflex", - "Udieresis", - "Ugrave", - "Yacute", - "Ydieresis", - "Zcaron", - "aacute", - "acircumflex", - "adieresis", - "agrave", - "aring", - "atilde", - "ccedilla", - "eacute", - "ecircumflex", - "edieresis", - "egrave", - "iacute", - "icircumflex", - "idieresis", - "igrave", - "ntilde", - "oacute", - "ocircumflex", - "odieresis", - "ograve", - "otilde", - "scaron", - "uacute", - "ucircumflex", - "udieresis", - "ugrave", - "yacute", - "ydieresis", - "zcaron", - "exclamsmall", - "Hungarumlautsmall", - "dollaroldstyle", - "dollarsuperior", - "ampersandsmall", - "Acutesmall", - "parenleftsuperior", - "parenrightsuperior", - "twodotenleader", - "onedotenleader", - "zerooldstyle", - "oneoldstyle", - "twooldstyle", - "threeoldstyle", - "fouroldstyle", - "fiveoldstyle", - "sixoldstyle", - "sevenoldstyle", - "eightoldstyle", - "nineoldstyle", - "commasuperior", - "threequartersemdash", - "periodsuperior", - "questionsmall", - "asuperior", - "bsuperior", - "centsuperior", - "dsuperior", - "esuperior", - "isuperior", - "lsuperior", - "msuperior", - "nsuperior", - "osuperior", - "rsuperior", - "ssuperior", - "tsuperior", - "ff", - "ffi", - "ffl", - "parenleftinferior", - "parenrightinferior", - "Circumflexsmall", - "hyphensuperior", - "Gravesmall", - "Asmall", - "Bsmall", - "Csmall", - "Dsmall", - "Esmall", - "Fsmall", - "Gsmall", - "Hsmall", - "Ismall", - "Jsmall", - "Ksmall", - "Lsmall", - "Msmall", - "Nsmall", - "Osmall", - "Psmall", - "Qsmall", - "Rsmall", - "Ssmall", - "Tsmall", - "Usmall", - "Vsmall", - "Wsmall", - "Xsmall", - "Ysmall", - "Zsmall", - "colonmonetary", - "onefitted", - "rupiah", - "Tildesmall", - "exclamdownsmall", - "centoldstyle", - "Lslashsmall", - "Scaronsmall", - "Zcaronsmall", - "Dieresissmall", - "Brevesmall", - "Caronsmall", - "Dotaccentsmall", - "Macronsmall", - "figuredash", - "hypheninferior", - "Ogoneksmall", - "Ringsmall", - "Cedillasmall", - "questiondownsmall", - "oneeighth", - "threeeighths", - "fiveeighths", - "seveneighths", - "onethird", - "twothirds", - "zerosuperior", - "foursuperior", - "fivesuperior", - "sixsuperior", - "sevensuperior", - "eightsuperior", - "ninesuperior", - "zeroinferior", - "oneinferior", - "twoinferior", - "threeinferior", - "fourinferior", - "fiveinferior", - "sixinferior", - "seveninferior", - "eightinferior", - "nineinferior", - "centinferior", - "dollarinferior", - "periodinferior", - "commainferior", - "Agravesmall", - "Aacutesmall", - "Acircumflexsmall", - "Atildesmall", - "Adieresissmall", - "Aringsmall", - "AEsmall", - "Ccedillasmall", - "Egravesmall", - "Eacutesmall", - "Ecircumflexsmall", - "Edieresissmall", - "Igravesmall", - "Iacutesmall", - "Icircumflexsmall", - "Idieresissmall", - "Ethsmall", - "Ntildesmall", - "Ogravesmall", - "Oacutesmall", - "Ocircumflexsmall", - "Otildesmall", - "Odieresissmall", - "OEsmall", - "Oslashsmall", - "Ugravesmall", - "Uacutesmall", - "Ucircumflexsmall", - "Udieresissmall", - "Yacutesmall", - "Thornsmall", - "Ydieresissmall", - "001.000", - "001.001", - "001.002", - "001.003", - "Black", - "Bold", - "Book", - "Light", - "Medium", - "Regular", - "Roman", - "Semibold" - }; -} diff --git a/src/net/java/dev/typecast/ot/table/CffTable.java b/src/net/java/dev/typecast/ot/table/CffTable.java index 2ed84e2..4136c7c 100644 --- a/src/net/java/dev/typecast/ot/table/CffTable.java +++ b/src/net/java/dev/typecast/ot/table/CffTable.java @@ -22,11 +22,19 @@ import java.io.ByteArrayInputStream; import java.io.DataInput; import java.io.DataInputStream; import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; import java.util.List; -import java.util.Map; +import net.java.dev.typecast.t2.CffFont; +import net.java.dev.typecast.t2.Charset; +import net.java.dev.typecast.t2.CharsetFormat0; +import net.java.dev.typecast.t2.CharsetFormat1; +import net.java.dev.typecast.t2.CharsetFormat2; +import net.java.dev.typecast.t2.Charstring; +import net.java.dev.typecast.t2.CharstringType2; +import net.java.dev.typecast.t2.Dict; +import net.java.dev.typecast.t2.Index; +import net.java.dev.typecast.t2.NameIndex; +import net.java.dev.typecast.t2.StringIndex; +import net.java.dev.typecast.t2.TopDictIndex; /** * Compact Font Format Table @@ -34,496 +42,6 @@ import java.util.Map; */ public class CffTable implements Table { - public static class Dict { - - private final Map _entries = new HashMap<>(); - private final int[] _data; - private int _index; - - protected Dict(int[] data, int offset, int length) { - _data = data; - _index = offset; - while (_index < offset + length) { - addKeyAndValueEntry(); - } - } - - protected Dict(DataInput di, int length) throws IOException { - _data = new int[length]; - for (int i = 0; i < length; ++i) { - _data[i] = di.readUnsignedByte(); - } - _index = 0; - while (_index < length) { - addKeyAndValueEntry(); - } - } - - public Object getValue(int key) { - return _entries.get(key); - } - - private boolean addKeyAndValueEntry() { - ArrayList operands = new ArrayList<>(); - Object operand = null; - while (isOperandAtIndex()) { - operand = nextOperand(); - operands.add(operand); - } - int operator = _data[_index++]; - if (operator == 12) { - operator <<= 8; - operator |= _data[_index++]; - } - if (operands.size() == 1) { - _entries.put(operator, operand); - } else { - _entries.put(operator, operands); - } - return true; - } - - private boolean isOperandAtIndex() { - int b0 = _data[_index]; - return (32 <= b0 && b0 <= 254) - || b0 == 28 - || b0 == 29 - || b0 == 30; - } - -// private boolean isOperatorAtIndex() { -// int b0 = _data[_index]; -// return 0 <= b0 && b0 <= 21; -// } - - private Object nextOperand() { - int b0 = _data[_index]; - if (32 <= b0 && b0 <= 246) { - - // 1 byte integer - ++_index; - return b0 - 139; - } else if (247 <= b0 && b0 <= 250) { - - // 2 byte integer - int b1 = _data[_index + 1]; - _index += 2; - return (b0 - 247) * 256 + b1 + 108; - } else if (251 <= b0 && b0 <= 254) { - - // 2 byte integer - int b1 = _data[_index + 1]; - _index += 2; - return -(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 b1 << 8 | b2; - } else if (b0 == 29) { - - // 5 byte integer - int b1 = _data[_index + 1]; - int b2 = _data[_index + 2]; - int b3 = _data[_index + 3]; - int b4 = _data[_index + 4]; - _index += 5; - return b1 << 24 | b2 << 16 | b3 << 8 | b4; - } else if (b0 == 30) { - - // Real number - StringBuilder fString = new StringBuilder(); - int nibble1 = 0; - int nibble2 = 0; - ++_index; - while ((nibble1 != 0xf) && (nibble2 != 0xf)) { - nibble1 = _data[_index] >> 4; - nibble2 = _data[_index] & 0xf; - ++_index; - fString.append(decodeRealNibble(nibble1)); - fString.append(decodeRealNibble(nibble2)); - } - return Float.valueOf(fString.toString()); - } else { - return null; - } - } - - private String decodeRealNibble(int nibble) { - if (nibble < 0xa) { - return Integer.toString(nibble); - } else if (nibble == 0xa) { - return "."; - } else if (nibble == 0xb) { - return "E"; - } else if (nibble == 0xc) { - return "E-"; - } else if (nibble == 0xe) { - return "-"; - } - return ""; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - Iterator keys = _entries.keySet().iterator(); - while (keys.hasNext()) { - Integer key = keys.next(); - if ((key & 0xc00) == 0xc00) { - sb.append("12 ").append(key & 0xff).append(": "); - } else { - sb.append(key.toString()).append(": "); - } - sb.append(_entries.get(key).toString()).append("\n"); - } - return sb.toString(); - } - } - - public class Index { - - private final int _count; - private final int _offSize; - private final int[] _offset; - private final int[] _data; - - protected Index(DataInput di) throws IOException { - _count = di.readUnsignedShort(); - _offset = new int[_count + 1]; - _offSize = di.readUnsignedByte(); - for (int i = 0; i < _count + 1; ++i) { - int thisOffset = 0; - for (int j = 0; j < _offSize; ++j) { - thisOffset |= di.readUnsignedByte() << ((_offSize - j - 1) * 8); - } - _offset[i] = thisOffset; - } - _data = new int[getDataLength()]; - for (int i = 0; i < getDataLength(); ++i) { - _data[i] = di.readUnsignedByte(); - } - } - - public final int getCount() { - return _count; - } - - public final int getOffset(int index) { - return _offset[index]; - } - - public final int getDataLength() { - return _offset[_offset.length - 1] - 1; - } - - public final int[] getData() { - return _data; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("DICT\n"); - sb.append("count: ").append(_count).append("\n"); - sb.append("offSize: ").append(_offSize).append("\n"); - for (int i = 0; i < _count + 1; ++i) { - sb.append("offset[").append(i).append("]: ").append(_offset[i]).append("\n"); - } - sb.append("data:"); - for (int i = 0; i < _data.length; ++i) { - if (i % 8 == 0) { - sb.append("\n"); - } else { - sb.append(" "); - } - sb.append(_data[i]); - } - sb.append("\n"); - return sb.toString(); - } - } - - public class TopDictIndex extends Index { - - protected TopDictIndex(DataInput di) throws IOException { - super(di); - } - - public Dict getTopDict(int index) { - int offset = getOffset(index) - 1; - int len = getOffset(index + 1) - offset - 1; - return new Dict(getData(), offset, len); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < getCount(); ++i) { - sb.append(getTopDict(i).toString()).append("\n"); - } - return sb.toString(); - } - } - - public class NameIndex extends Index { - - protected NameIndex(DataInput di) throws IOException { - super(di); - } - - public String getName(int index) { - String name = null; - int offset = getOffset(index) - 1; - int len = getOffset(index + 1) - offset - 1; - - // Ensure the name hasn't been deleted - if (getData()[offset] != 0) { - StringBuilder sb = new StringBuilder(); - for (int i = offset; i < offset + len; ++i) { - sb.append((char) getData()[i]); - } - name = sb.toString(); - } else { - name = "DELETED NAME"; - } - return name; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < getCount(); ++i) { - sb.append(getName(i)).append("\n"); - } - return sb.toString(); - } - } - - public class StringIndex extends Index { - - protected StringIndex(DataInput di) throws IOException { - super(di); - } - - public String getString(int index) { - if (index < CffStandardStrings.standardStrings.length) { - 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; - - StringBuilder sb = new StringBuilder(); - for (int i = offset; i < offset + len; ++i) { - sb.append((char) getData()[i]); - } - return sb.toString(); - } - } - - @Override - public String toString() { - int nonStandardBase = CffStandardStrings.standardStrings.length; - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < getCount(); ++i) { - sb.append(nonStandardBase + i).append(": "); - sb.append(getString(nonStandardBase + i)).append("\n"); - } - return sb.toString(); - } - } - - private class CharsetRange { - - private int _first; - private int _left; - - public final int getFirst() { - return _first; - } - - protected final void setFirst(int first) { - _first = first; - } - - public final int getLeft() { - return _left; - } - - protected final 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 final 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(); - } - } - - @Override - public int getFormat() { - return 0; - } - - @Override - public int getSID(int gid) { - if (gid == 0) { - return 0; - } - return _glyph[gid - 1]; - } - } - - private class CharsetFormat1 extends Charset { - - private final ArrayList _charsetRanges = new ArrayList<>(); - - 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; - } - } - - @Override - public int getFormat() { - return 1; - } - - @Override - public int getSID(int gid) { - if (gid == 0) { - return 0; - } - - // Count through the ranges to find the one of interest - int count = 1; - for (CharsetRange range : _charsetRanges) { - if (gid <= range.getLeft() + count) { - int sid = gid - count + range.getFirst(); - return sid; - } - count += range.getLeft() + 1; - } - return 0; - } - } - - private class CharsetFormat2 extends Charset { - - private final ArrayList _charsetRanges = new ArrayList<>(); - - 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; - } - } - - @Override - public int getFormat() { - return 2; - } - - @Override - public int getSID(int gid) { - if (gid == 0) { - return 0; - } - - // Count through the ranges to find the one of interest - int count = 1; - for (CharsetRange range : _charsetRanges) { - if (gid <= range.getLeft() + count) { - int sid = gid - count + range.getFirst(); - return sid; - } - count += range.getLeft() + 1; - } - return 0; - } - } - - public class CffFont { - private Index _charStringsIndex; - private Dict _privateDict; - private Index _localSubrsIndex; - private Charset _charset; - private Charstring[] _charstrings; - - public CffFont( - Index charStringsIndex, - Dict privateDict, - Index localSubrsIndex, - Charset charset, - Charstring[] charstrings) { - _charStringsIndex = charStringsIndex; - _privateDict = privateDict; - _localSubrsIndex = localSubrsIndex; - _charset = charset; - _charstrings = charstrings; - } - - public Index getCharStringsIndex() { - return _charStringsIndex; - } - - public Dict getPrivateDict() { - return _privateDict; - } - - public Index getLocalSubrsIndex() { - return _localSubrsIndex; - } - - public Charset getCharset() { - return _charset; - } - - public Charstring[] getCharstrings() { - return _charstrings; - } - } - private DirectoryEntry _de; private int _major; private int _minor; diff --git a/src/net/java/dev/typecast/ot/table/Charstring.java b/src/net/java/dev/typecast/ot/table/Charstring.java deleted file mode 100644 index 65fcdef..0000000 --- a/src/net/java/dev/typecast/ot/table/Charstring.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Typecast - The Font Development Environment - * - * Copyright (c) 2004-2015 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 - * @author David Schweinsberg - */ -public abstract class Charstring { - - public abstract int getIndex(); - - 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 deleted file mode 100644 index 31d0d61..0000000 --- a/src/net/java/dev/typecast/ot/table/CharstringType2.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Typecast - The Font Development Environment - * - * Copyright (c) 2004-2015 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 - * @author David Schweinsberg - */ -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 final int _index; - private final String _name; - private final int[] _data; - private final int _offset; - private final int _length; - private int _ip; - - /** Creates a new instance of CharstringType2 - * @param index - * @param name - * @param data - * @param offset - * @param length */ - protected CharstringType2( - int index, - String name, - int[] data, - int offset, - int length) { - _index = index; - _name = name; - _data = data; - _offset = offset; - _length = length; - } - - @Override - public int getIndex() { - return _index; - } - - @Override - public String getName() { - return _name; - } - - private void disassemble(StringBuilder sb) { - while (isOperandAtIndex()) { - Number operand = nextOperand(); - sb.append(operand).append(" "); - } - int operator = nextByte(); - String mnemonic; - if (operator == 12) { - operator = nextByte(); - - // 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); - } - - public void resetIP() { - _ip = _offset; - } - - public boolean isOperandAtIndex() { - int b0 = _data[_ip]; - return (32 <= b0 && b0 <= 255) || b0 == 28; - } - - public Number nextOperand() { - int b0 = _data[_ip]; - if (32 <= b0 && b0 <= 246) { - - // 1 byte integer - ++_ip; - return b0 - 139; - } else if (247 <= b0 && b0 <= 250) { - - // 2 byte integer - int b1 = _data[_ip + 1]; - _ip += 2; - return (b0 - 247) * 256 + b1 + 108; - } else if (251 <= b0 && b0 <= 254) { - - // 2 byte integer - int b1 = _data[_ip + 1]; - _ip += 2; - return -(b0 - 251) * 256 - b1 - 108; - } else if (b0 == 28) { - - // 3 byte integer - int b1 = (byte)_data[_ip + 1]; - int b2 = _data[_ip + 2]; - _ip += 3; - return b1 << 8 | b2; - } else if (b0 == 255) { - - // 16-bit signed integer with 16 bits of fraction - int b1 = (byte) _data[_ip + 1]; - int b2 = _data[_ip + 2]; - int b3 = _data[_ip + 3]; - int b4 = _data[_ip + 4]; - _ip += 5; - return new Float((b1 << 8 | b2) + ((b3 << 8 | b4) / 65536.0)); - } else { - return null; - } - } - - public int nextByte() { - return _data[_ip++]; - } - - public boolean moreBytes() { - return _ip < _offset + _length; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - resetIP(); - while (moreBytes()) { - disassemble(sb); - sb.append("\n"); - } - return sb.toString(); - } -} diff --git a/src/net/java/dev/typecast/t2/CffFont.java b/src/net/java/dev/typecast/t2/CffFont.java new file mode 100644 index 0000000..68d8bda --- /dev/null +++ b/src/net/java/dev/typecast/t2/CffFont.java @@ -0,0 +1,64 @@ +/* + * Typecast - The Font Development Environment + * + * Copyright (c) 2004-2015 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.t2; + +/** + * + * @author dschweinsberg + */ +public class CffFont { + + private final Index _charStringsIndex; + private final Dict _privateDict; + private final Index _localSubrsIndex; + private final Charset _charset; + private final Charstring[] _charstrings; + + public CffFont( + Index charStringsIndex, + Dict privateDict, + Index localSubrsIndex, + Charset charset, + Charstring[] charstrings) { + _charStringsIndex = charStringsIndex; + _privateDict = privateDict; + _localSubrsIndex = localSubrsIndex; + _charset = charset; + _charstrings = charstrings; + } + + public Index getCharStringsIndex() { + return _charStringsIndex; + } + + public Dict getPrivateDict() { + return _privateDict; + } + + public Index getLocalSubrsIndex() { + return _localSubrsIndex; + } + + public Charset getCharset() { + return _charset; + } + + public Charstring[] getCharstrings() { + return _charstrings; + } +} diff --git a/src/net/java/dev/typecast/t2/CffStandardStrings.java b/src/net/java/dev/typecast/t2/CffStandardStrings.java new file mode 100644 index 0000000..a2f8cca --- /dev/null +++ b/src/net/java/dev/typecast/t2/CffStandardStrings.java @@ -0,0 +1,421 @@ +/* + * Typecast - The Font Development Environment + * + * Copyright (c) 2004-2015 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.t2; + +/** + * Compact Font Format Standard Strings. As per Appendix A of the Adobe + * CFF specification. + * @author David Schweinsberg + */ +public class CffStandardStrings { + + public static final String[] standardStrings = { + ".notdef", + "space", + "exclam", + "quotedbl", + "numbersign", + "dollar", + "percent", + "ampersand", + "quoteright", + "parenleft", + "parenright", + "asterisk", + "plus", + "comma", + "hyphen", + "period", + "slash", + "zero", + "one", + "two", + "three", + "four", + "five", + "six", + "seven", + "eight", + "nine", + "colon", + "semicolon", + "less", + "equal", + "greater", + "question", + "at", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "bracketleft", + "backslash", + "bracketright", + "asciicircum", + "underscore", + "quoteleft", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "braceleft", + "bar", + "braceright", + "asciitilde", + "exclamdown", + "cent", + "sterling", + "fraction", + "yen", + "florin", + "section", + "currency", + "quotesingle", + "quotedblleft", + "guillemotleft", + "guilsinglleft", + "guilsinglright", + "fi", + "fl", + "endash", + "dagger", + "daggerdbl", + "periodcentered", + "paragraph", + "bullet", + "quotesinglbase", + "quotedblbase", + "quotedblright", + "guillemotright", + "ellipsis", + "perthousand", + "questiondown", + "grave", + "acute", + "circumflex", + "tilde", + "macron", + "breve", + "dotaccent", + "dieresis", + "ring", + "cedilla", + "hungarumlaut", + "ogonek", + "caron", + "emdash", + "AE", + "ordfeminine", + "Lslash", + "Oslash", + "OE", + "ordmasculine", + "ae", + "dotlessi", + "lslash", + "oslash", + "oe", + "germandbls", + "onesuperior", + "logicalnot", + "mu", + "trademark", + "Eth", + "onehalf", + "plusminus", + "Thorn", + "onequarter", + "divide", + "brokenbar", + "degree", + "thorn", + "threequarters", + "twosuperior", + "registered", + "minus", + "eth", + "multiply", + "threesuperior", + "copyright", + "Aacute", + "Acircumflex", + "Adieresis", + "Agrave", + "Aring", + "Atilde", + "Ccedilla", + "Eacute", + "Ecircumflex", + "Edieresis", + "Egrave", + "Iacute", + "Icircumflex", + "Idieresis", + "Igrave", + "Ntilde", + "Oacute", + "Ocircumflex", + "Odieresis", + "Ograve", + "Otilde", + "Scaron", + "Uacute", + "Ucircumflex", + "Udieresis", + "Ugrave", + "Yacute", + "Ydieresis", + "Zcaron", + "aacute", + "acircumflex", + "adieresis", + "agrave", + "aring", + "atilde", + "ccedilla", + "eacute", + "ecircumflex", + "edieresis", + "egrave", + "iacute", + "icircumflex", + "idieresis", + "igrave", + "ntilde", + "oacute", + "ocircumflex", + "odieresis", + "ograve", + "otilde", + "scaron", + "uacute", + "ucircumflex", + "udieresis", + "ugrave", + "yacute", + "ydieresis", + "zcaron", + "exclamsmall", + "Hungarumlautsmall", + "dollaroldstyle", + "dollarsuperior", + "ampersandsmall", + "Acutesmall", + "parenleftsuperior", + "parenrightsuperior", + "twodotenleader", + "onedotenleader", + "zerooldstyle", + "oneoldstyle", + "twooldstyle", + "threeoldstyle", + "fouroldstyle", + "fiveoldstyle", + "sixoldstyle", + "sevenoldstyle", + "eightoldstyle", + "nineoldstyle", + "commasuperior", + "threequartersemdash", + "periodsuperior", + "questionsmall", + "asuperior", + "bsuperior", + "centsuperior", + "dsuperior", + "esuperior", + "isuperior", + "lsuperior", + "msuperior", + "nsuperior", + "osuperior", + "rsuperior", + "ssuperior", + "tsuperior", + "ff", + "ffi", + "ffl", + "parenleftinferior", + "parenrightinferior", + "Circumflexsmall", + "hyphensuperior", + "Gravesmall", + "Asmall", + "Bsmall", + "Csmall", + "Dsmall", + "Esmall", + "Fsmall", + "Gsmall", + "Hsmall", + "Ismall", + "Jsmall", + "Ksmall", + "Lsmall", + "Msmall", + "Nsmall", + "Osmall", + "Psmall", + "Qsmall", + "Rsmall", + "Ssmall", + "Tsmall", + "Usmall", + "Vsmall", + "Wsmall", + "Xsmall", + "Ysmall", + "Zsmall", + "colonmonetary", + "onefitted", + "rupiah", + "Tildesmall", + "exclamdownsmall", + "centoldstyle", + "Lslashsmall", + "Scaronsmall", + "Zcaronsmall", + "Dieresissmall", + "Brevesmall", + "Caronsmall", + "Dotaccentsmall", + "Macronsmall", + "figuredash", + "hypheninferior", + "Ogoneksmall", + "Ringsmall", + "Cedillasmall", + "questiondownsmall", + "oneeighth", + "threeeighths", + "fiveeighths", + "seveneighths", + "onethird", + "twothirds", + "zerosuperior", + "foursuperior", + "fivesuperior", + "sixsuperior", + "sevensuperior", + "eightsuperior", + "ninesuperior", + "zeroinferior", + "oneinferior", + "twoinferior", + "threeinferior", + "fourinferior", + "fiveinferior", + "sixinferior", + "seveninferior", + "eightinferior", + "nineinferior", + "centinferior", + "dollarinferior", + "periodinferior", + "commainferior", + "Agravesmall", + "Aacutesmall", + "Acircumflexsmall", + "Atildesmall", + "Adieresissmall", + "Aringsmall", + "AEsmall", + "Ccedillasmall", + "Egravesmall", + "Eacutesmall", + "Ecircumflexsmall", + "Edieresissmall", + "Igravesmall", + "Iacutesmall", + "Icircumflexsmall", + "Idieresissmall", + "Ethsmall", + "Ntildesmall", + "Ogravesmall", + "Oacutesmall", + "Ocircumflexsmall", + "Otildesmall", + "Odieresissmall", + "OEsmall", + "Oslashsmall", + "Ugravesmall", + "Uacutesmall", + "Ucircumflexsmall", + "Udieresissmall", + "Yacutesmall", + "Thornsmall", + "Ydieresissmall", + "001.000", + "001.001", + "001.002", + "001.003", + "Black", + "Bold", + "Book", + "Light", + "Medium", + "Regular", + "Roman", + "Semibold" + }; +} diff --git a/src/net/java/dev/typecast/t2/Charset.java b/src/net/java/dev/typecast/t2/Charset.java new file mode 100644 index 0000000..e221828 --- /dev/null +++ b/src/net/java/dev/typecast/t2/Charset.java @@ -0,0 +1,33 @@ +/* + * Typecast - The Font Development Environment + * + * Copyright (c) 2004-2015 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.t2; + +/** + * + * @author dschweinsberg + */ +public abstract class Charset { + + Charset() { + } + + public abstract int getFormat(); + + public abstract int getSID(int gid); + +} diff --git a/src/net/java/dev/typecast/t2/CharsetFormat0.java b/src/net/java/dev/typecast/t2/CharsetFormat0.java new file mode 100644 index 0000000..dfca79b --- /dev/null +++ b/src/net/java/dev/typecast/t2/CharsetFormat0.java @@ -0,0 +1,51 @@ +/* + * Typecast - The Font Development Environment + * + * Copyright (c) 2004-2015 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.t2; + +import java.io.DataInput; +import java.io.IOException; + +/** + * + * @author dschweinsberg + */ +public class CharsetFormat0 extends Charset { + + private final int[] _glyph; + + public 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(); + } + } // minus 1 because .notdef is omitted + + @Override + public int getFormat() { + return 0; + } + + @Override + public int getSID(int gid) { + if (gid == 0) { + return 0; + } + return _glyph[gid - 1]; + } + +} diff --git a/src/net/java/dev/typecast/t2/CharsetFormat1.java b/src/net/java/dev/typecast/t2/CharsetFormat1.java new file mode 100644 index 0000000..1a0a64f --- /dev/null +++ b/src/net/java/dev/typecast/t2/CharsetFormat1.java @@ -0,0 +1,63 @@ +/* + * Typecast - The Font Development Environment + * + * Copyright (c) 2004-2015 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.t2; + +import java.io.DataInput; +import java.io.IOException; +import java.util.ArrayList; + +/** + * + * @author dschweinsberg + */ +public class CharsetFormat1 extends Charset { + + private final ArrayList _charsetRanges = new ArrayList<>(); + + public 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; + } + } + + @Override + public int getFormat() { + return 1; + } + + @Override + public int getSID(int gid) { + if (gid == 0) { + return 0; + } + + // Count through the ranges to find the one of interest + int count = 1; + for (CharsetRange range : _charsetRanges) { + if (gid <= range.getLeft() + count) { + int sid = gid - count + range.getFirst(); + return sid; + } + count += range.getLeft() + 1; + } + return 0; + } +} diff --git a/src/net/java/dev/typecast/t2/CharsetFormat2.java b/src/net/java/dev/typecast/t2/CharsetFormat2.java new file mode 100644 index 0000000..6ccdd86 --- /dev/null +++ b/src/net/java/dev/typecast/t2/CharsetFormat2.java @@ -0,0 +1,63 @@ +/* + * Typecast - The Font Development Environment + * + * Copyright (c) 2004-2015 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.t2; + +import java.io.DataInput; +import java.io.IOException; +import java.util.ArrayList; + +/** + * + * @author dschweinsberg + */ +public class CharsetFormat2 extends Charset { + + private final ArrayList _charsetRanges = new ArrayList<>(); + + public 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; + } + } + + @Override + public int getFormat() { + return 2; + } + + @Override + public int getSID(int gid) { + if (gid == 0) { + return 0; + } + + // Count through the ranges to find the one of interest + int count = 1; + for (CharsetRange range : _charsetRanges) { + if (gid <= range.getLeft() + count) { + int sid = gid - count + range.getFirst(); + return sid; + } + count += range.getLeft() + 1; + } + return 0; + } +} diff --git a/src/net/java/dev/typecast/t2/CharsetRange.java b/src/net/java/dev/typecast/t2/CharsetRange.java new file mode 100644 index 0000000..452adc3 --- /dev/null +++ b/src/net/java/dev/typecast/t2/CharsetRange.java @@ -0,0 +1,48 @@ +/* + * Typecast - The Font Development Environment + * + * Copyright (c) 2004-2015 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.t2; + +/** + * + * @author dschweinsberg + */ +public class CharsetRange { + + private int _first; + private int _left; + + CharsetRange() { + } + + public final int getFirst() { + return _first; + } + + protected final void setFirst(int first) { + _first = first; + } + + public final int getLeft() { + return _left; + } + + protected final void setLeft(int left) { + _left = left; + } + +} diff --git a/src/net/java/dev/typecast/t2/CharsetRange1.java b/src/net/java/dev/typecast/t2/CharsetRange1.java new file mode 100644 index 0000000..dff99d8 --- /dev/null +++ b/src/net/java/dev/typecast/t2/CharsetRange1.java @@ -0,0 +1,34 @@ +/* + * Typecast - The Font Development Environment + * + * Copyright (c) 2004-2015 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.t2; + +import java.io.DataInput; +import java.io.IOException; + +/** + * + * @author dschweinsberg + */ +public class CharsetRange1 extends CharsetRange { + + protected CharsetRange1(DataInput di) throws IOException { + setFirst(di.readUnsignedShort()); + setLeft(di.readUnsignedByte()); + } + +} diff --git a/src/net/java/dev/typecast/t2/CharsetRange2.java b/src/net/java/dev/typecast/t2/CharsetRange2.java new file mode 100644 index 0000000..18b2baf --- /dev/null +++ b/src/net/java/dev/typecast/t2/CharsetRange2.java @@ -0,0 +1,34 @@ +/* + * Typecast - The Font Development Environment + * + * Copyright (c) 2004-2015 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.t2; + +import java.io.DataInput; +import java.io.IOException; + +/** + * + * @author dschweinsberg + */ +public class CharsetRange2 extends CharsetRange { + + protected CharsetRange2(DataInput di) throws IOException { + setFirst(di.readUnsignedShort()); + setLeft(di.readUnsignedShort()); + } + +} diff --git a/src/net/java/dev/typecast/t2/Charstring.java b/src/net/java/dev/typecast/t2/Charstring.java new file mode 100644 index 0000000..36b8f77 --- /dev/null +++ b/src/net/java/dev/typecast/t2/Charstring.java @@ -0,0 +1,30 @@ +/* + * Typecast - The Font Development Environment + * + * Copyright (c) 2004-2015 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.t2; + +/** + * CFF Charstring + * @author David Schweinsberg + */ +public abstract class Charstring { + + public abstract int getIndex(); + + public abstract String getName(); +} diff --git a/src/net/java/dev/typecast/t2/CharstringType2.java b/src/net/java/dev/typecast/t2/CharstringType2.java new file mode 100644 index 0000000..eb8bc69 --- /dev/null +++ b/src/net/java/dev/typecast/t2/CharstringType2.java @@ -0,0 +1,228 @@ +/* + * Typecast - The Font Development Environment + * + * Copyright (c) 2004-2015 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.t2; + +/** + * CFF Type 2 Charstring + * @author David Schweinsberg + */ +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 final int _index; + private final String _name; + private final int[] _data; + private final int _offset; + private final int _length; + private int _ip; + + /** Creates a new instance of CharstringType2 + * @param index + * @param name + * @param data + * @param offset + * @param length */ + public CharstringType2( + int index, + String name, + int[] data, + int offset, + int length) { + _index = index; + _name = name; + _data = data; + _offset = offset; + _length = length; + } + + @Override + public int getIndex() { + return _index; + } + + @Override + public String getName() { + return _name; + } + + private void disassemble(StringBuilder sb) { + while (isOperandAtIndex()) { + Number operand = nextOperand(); + sb.append(operand).append(" "); + } + int operator = nextByte(); + String mnemonic; + if (operator == 12) { + operator = nextByte(); + + // 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); + } + + public void resetIP() { + _ip = _offset; + } + + public boolean isOperandAtIndex() { + int b0 = _data[_ip]; + return (32 <= b0 && b0 <= 255) || b0 == 28; + } + + public Number nextOperand() { + int b0 = _data[_ip]; + if (32 <= b0 && b0 <= 246) { + + // 1 byte integer + ++_ip; + return b0 - 139; + } else if (247 <= b0 && b0 <= 250) { + + // 2 byte integer + int b1 = _data[_ip + 1]; + _ip += 2; + return (b0 - 247) * 256 + b1 + 108; + } else if (251 <= b0 && b0 <= 254) { + + // 2 byte integer + int b1 = _data[_ip + 1]; + _ip += 2; + return -(b0 - 251) * 256 - b1 - 108; + } else if (b0 == 28) { + + // 3 byte integer + int b1 = (byte)_data[_ip + 1]; + int b2 = _data[_ip + 2]; + _ip += 3; + return b1 << 8 | b2; + } else if (b0 == 255) { + + // 16-bit signed integer with 16 bits of fraction + int b1 = (byte) _data[_ip + 1]; + int b2 = _data[_ip + 2]; + int b3 = _data[_ip + 3]; + int b4 = _data[_ip + 4]; + _ip += 5; + return new Float((b1 << 8 | b2) + ((b3 << 8 | b4) / 65536.0)); + } else { + return null; + } + } + + public int nextByte() { + return _data[_ip++]; + } + + public boolean moreBytes() { + return _ip < _offset + _length; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + resetIP(); + while (moreBytes()) { + disassemble(sb); + sb.append("\n"); + } + return sb.toString(); + } +} diff --git a/src/net/java/dev/typecast/t2/Dict.java b/src/net/java/dev/typecast/t2/Dict.java new file mode 100644 index 0000000..d4c6d79 --- /dev/null +++ b/src/net/java/dev/typecast/t2/Dict.java @@ -0,0 +1,169 @@ +/* + * Typecast - The Font Development Environment + * + * Copyright (c) 2004-2015 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.t2; + +import java.io.DataInput; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * + * @author dschweinsberg + */ +public class Dict { + + private final Map _entries = new HashMap<>(); + private final int[] _data; + private int _index; + + public Dict(int[] data, int offset, int length) { + _data = data; + _index = offset; + while (_index < offset + length) { + addKeyAndValueEntry(); + } + } + + public Dict(DataInput di, int length) throws IOException { + _data = new int[length]; + for (int i = 0; i < length; ++i) { + _data[i] = di.readUnsignedByte(); + } + _index = 0; + while (_index < length) { + addKeyAndValueEntry(); + } + } + + public Object getValue(int key) { + return _entries.get(key); + } + + private boolean addKeyAndValueEntry() { + ArrayList operands = new ArrayList<>(); + Object operand = null; + while (isOperandAtIndex()) { + operand = nextOperand(); + operands.add(operand); + } + int operator = _data[_index++]; + if (operator == 12) { + operator <<= 8; + operator |= _data[_index++]; + } + if (operands.size() == 1) { + _entries.put(operator, operand); + } else { + _entries.put(operator, operands); + } + return true; + } + + private boolean isOperandAtIndex() { + int b0 = _data[_index]; + return (32 <= b0 && b0 <= 254) || b0 == 28 || b0 == 29 || b0 == 30; + } + + // private boolean isOperatorAtIndex() { + // int b0 = _data[_index]; + // return 0 <= b0 && b0 <= 21; + // } + private Object nextOperand() { + int b0 = _data[_index]; + if (32 <= b0 && b0 <= 246) { + // 1 byte integer + ++_index; + return b0 - 139; + } else if (247 <= b0 && b0 <= 250) { + // 2 byte integer + int b1 = _data[_index + 1]; + _index += 2; + return (b0 - 247) * 256 + b1 + 108; + } else if (251 <= b0 && b0 <= 254) { + // 2 byte integer + int b1 = _data[_index + 1]; + _index += 2; + return -(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 b1 << 8 | b2; + } else if (b0 == 29) { + // 5 byte integer + int b1 = _data[_index + 1]; + int b2 = _data[_index + 2]; + int b3 = _data[_index + 3]; + int b4 = _data[_index + 4]; + _index += 5; + return b1 << 24 | b2 << 16 | b3 << 8 | b4; + } else if (b0 == 30) { + // Real number + StringBuilder fString = new StringBuilder(); + int nibble1 = 0; + int nibble2 = 0; + ++_index; + while ((nibble1 != 0xf) && (nibble2 != 0xf)) { + nibble1 = _data[_index] >> 4; + nibble2 = _data[_index] & 0xf; + ++_index; + fString.append(decodeRealNibble(nibble1)); + fString.append(decodeRealNibble(nibble2)); + } + return Float.valueOf(fString.toString()); + } else { + return null; + } + } + + private String decodeRealNibble(int nibble) { + if (nibble < 0xa) { + return Integer.toString(nibble); + } else if (nibble == 0xa) { + return "."; + } else if (nibble == 0xb) { + return "E"; + } else if (nibble == 0xc) { + return "E-"; + } else if (nibble == 0xe) { + return "-"; + } + return ""; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + Iterator keys = _entries.keySet().iterator(); + while (keys.hasNext()) { + Integer key = keys.next(); + if ((key & 0xc00) == 0xc00) { + sb.append("12 ").append(key & 0xff).append(": "); + } else { + sb.append(key.toString()).append(": "); + } + sb.append(_entries.get(key).toString()).append("\n"); + } + return sb.toString(); + } + +} diff --git a/src/net/java/dev/typecast/t2/Index.java b/src/net/java/dev/typecast/t2/Index.java new file mode 100644 index 0000000..dcad83b --- /dev/null +++ b/src/net/java/dev/typecast/t2/Index.java @@ -0,0 +1,89 @@ +/* + * Typecast - The Font Development Environment + * + * Copyright (c) 2004-2015 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.t2; + +import java.io.DataInput; +import java.io.IOException; + +/** + * + * @author dschweinsberg + */ +public class Index { + + private final int _count; + private final int _offSize; + private final int[] _offset; + private final int[] _data; + + public Index(DataInput di) throws IOException { + _count = di.readUnsignedShort(); + _offset = new int[_count + 1]; + _offSize = di.readUnsignedByte(); + for (int i = 0; i < _count + 1; ++i) { + int thisOffset = 0; + for (int j = 0; j < _offSize; ++j) { + thisOffset |= di.readUnsignedByte() << ((_offSize - j - 1) * 8); + } + _offset[i] = thisOffset; + } + _data = new int[getDataLength()]; + for (int i = 0; i < getDataLength(); ++i) { + _data[i] = di.readUnsignedByte(); + } + } + + public final int getCount() { + return _count; + } + + public final int getOffset(int index) { + return _offset[index]; + } + + public final int getDataLength() { + return _offset[_offset.length - 1] - 1; + } + + public final int[] getData() { + return _data; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("DICT\n"); + sb.append("count: ").append(_count).append("\n"); + sb.append("offSize: ").append(_offSize).append("\n"); + for (int i = 0; i < _count + 1; ++i) { + sb.append("offset[").append(i).append("]: ").append(_offset[i]).append("\n"); + } + sb.append("data:"); + for (int i = 0; i < _data.length; ++i) { + if (i % 8 == 0) { + sb.append("\n"); + } else { + sb.append(" "); + } + sb.append(_data[i]); + } + sb.append("\n"); + return sb.toString(); + } + +} diff --git a/src/net/java/dev/typecast/t2/NameIndex.java b/src/net/java/dev/typecast/t2/NameIndex.java new file mode 100644 index 0000000..3831070 --- /dev/null +++ b/src/net/java/dev/typecast/t2/NameIndex.java @@ -0,0 +1,57 @@ +/* + * Copyright 2015 dschweinsberg. + * + * 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.t2; + +import java.io.DataInput; +import java.io.IOException; + +/** + * + * @author dschweinsberg + */ +public class NameIndex extends Index { + + public NameIndex(DataInput di) throws IOException { + super(di); + } + + public String getName(int index) { + String name = null; + int offset = getOffset(index) - 1; + int len = getOffset(index + 1) - offset - 1; + // Ensure the name hasn't been deleted + if (getData()[offset] != 0) { + StringBuilder sb = new StringBuilder(); + for (int i = offset; i < offset + len; ++i) { + sb.append((char) getData()[i]); + } + name = sb.toString(); + } else { + name = "DELETED NAME"; + } + return name; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < getCount(); ++i) { + sb.append(getName(i)).append("\n"); + } + return sb.toString(); + } + +} diff --git a/src/net/java/dev/typecast/t2/StringIndex.java b/src/net/java/dev/typecast/t2/StringIndex.java new file mode 100644 index 0000000..ece39ab --- /dev/null +++ b/src/net/java/dev/typecast/t2/StringIndex.java @@ -0,0 +1,62 @@ +/* + * Typecast - The Font Development Environment + * + * Copyright (c) 2004-2015 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.t2; + +import java.io.DataInput; +import java.io.IOException; + +/** + * + * @author dschweinsberg + */ +public class StringIndex extends Index { + + public StringIndex(DataInput di) throws IOException { + super(di); + } + + public String getString(int index) { + if (index < CffStandardStrings.standardStrings.length) { + 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; + StringBuilder sb = new StringBuilder(); + for (int i = offset; i < offset + len; ++i) { + sb.append((char) getData()[i]); + } + return sb.toString(); + } + } + + @Override + public String toString() { + int nonStandardBase = CffStandardStrings.standardStrings.length; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < getCount(); ++i) { + sb.append(nonStandardBase + i).append(": "); + sb.append(getString(nonStandardBase + i)).append("\n"); + } + return sb.toString(); + } + +} diff --git a/src/net/java/dev/typecast/t2/T2Interpreter.java b/src/net/java/dev/typecast/t2/T2Interpreter.java index c3d05ae..f15856d 100644 --- a/src/net/java/dev/typecast/t2/T2Interpreter.java +++ b/src/net/java/dev/typecast/t2/T2Interpreter.java @@ -20,8 +20,6 @@ package net.java.dev.typecast.t2; import java.util.ArrayList; import net.java.dev.typecast.ot.Point; -import net.java.dev.typecast.ot.table.CffTable; -import net.java.dev.typecast.ot.table.CharstringType2; /** * Type 2 Charstring Interpreter. Operator descriptions are quoted from @@ -43,11 +41,11 @@ public class T2Interpreter { private int _stemCount = 0; private ArrayList _points; - private final CffTable.Index _localSubrIndex; - private final CffTable.Index _globalSubrIndex; + private final Index _localSubrIndex; + private final Index _globalSubrIndex; /** Creates a new instance of T2Interpreter */ - public T2Interpreter(CffTable.Index localSubrIndex, CffTable.Index globalSubrIndex) { + public T2Interpreter(Index localSubrIndex, Index globalSubrIndex) { _localSubrIndex = localSubrIndex; _globalSubrIndex = globalSubrIndex; } diff --git a/src/net/java/dev/typecast/t2/TopDictIndex.java b/src/net/java/dev/typecast/t2/TopDictIndex.java new file mode 100644 index 0000000..cd22466 --- /dev/null +++ b/src/net/java/dev/typecast/t2/TopDictIndex.java @@ -0,0 +1,48 @@ +/* + * Typecast - The Font Development Environment + * + * Copyright (c) 2004-2015 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.t2; + +import java.io.DataInput; +import java.io.IOException; + +/** + * + * @author dschweinsberg + */ +public class TopDictIndex extends Index { + + public TopDictIndex(DataInput di) throws IOException { + super(di); + } + + public Dict getTopDict(int index) { + int offset = getOffset(index) - 1; + int len = getOffset(index + 1) - offset - 1; + return new Dict(getData(), offset, len); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < getCount(); ++i) { + sb.append(getTopDict(i).toString()).append("\n"); + } + return sb.toString(); + } + +} -- cgit v1.2.3