001: /*
002: * Copyright (c) 2007, intarsys consulting GmbH
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * - Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * - Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * - Neither the name of intarsys nor the names of its contributors may be used
015: * to endorse or promote products derived from this software without specific
016: * prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028: * POSSIBILITY OF SUCH DAMAGE.
029: */
030: package de.intarsys.font.truetype;
031:
032: import java.io.IOException;
033: import java.io.OutputStream;
034:
035: import de.intarsys.tools.randomaccess.IRandomAccess;
036: import de.intarsys.tools.stream.StreamTools;
037:
038: /**
039: *
040: */
041: public class TTFontSerializer {
042: private OutputStream outputStream;
043:
044: private TTFont font;
045:
046: private int tableOffset = 0;
047:
048: public TTFontSerializer(OutputStream os) {
049: super ();
050: this .outputStream = os;
051: }
052:
053: public TTFont getFont() {
054: return font;
055: }
056:
057: public OutputStream getOutputStream() {
058: return outputStream;
059: }
060:
061: public void write(TTFont pFont) throws IOException {
062: this .font = pFont;
063: writeFontHeader();
064: writeTableDirectory();
065: writeTables();
066: }
067:
068: public void write_loca(int[] locations, boolean shortFormat)
069: throws IOException {
070: for (int i = 0; i < locations.length; i++) {
071: if (shortFormat) {
072: writeShort(getOutputStream(), locations[i] >> 1);
073: } else {
074: writeInt(getOutputStream(), locations[i]);
075: }
076: }
077: }
078:
079: public void write_name(TTNameRecord[] names) throws IOException {
080: int offset = 6 + (names.length * 12);
081: writeShort(getOutputStream(), 0);
082: writeShort(getOutputStream(), names.length);
083: writeShort(getOutputStream(), offset);
084: int nameOffset = 0;
085: for (int i = 0; i < names.length; i++) {
086: writeShort(getOutputStream(), names[i].getPlatformID());
087: writeShort(getOutputStream(), names[i].getEncodingID());
088: writeShort(getOutputStream(), names[i].getLanguageID());
089: writeShort(getOutputStream(), names[i].getNameID());
090: writeShort(getOutputStream(), names[i].getLength());
091: writeShort(getOutputStream(), nameOffset);
092: nameOffset += names[i].getLength();
093: }
094: for (int i = 0; i < names.length; i++) {
095: writeBytes(getOutputStream(), names[i].getValue().getBytes(
096: "UTF-16BE"), 0, names[i].getLength()); //$NON-NLS-1$
097: }
098: }
099:
100: protected void writeByte(OutputStream os, int value)
101: throws IOException {
102: os.write(value);
103: }
104:
105: protected void writeBytes(OutputStream os, byte[] value, int off,
106: int len) throws IOException {
107: os.write(value, off, len);
108: }
109:
110: /**
111: * <pre>
112: * Fixed sfnt version 0x00010000 for version 1.0.
113: * USHORT numTables Number of tables.
114: * USHORT searchRange (Maximum power of 2 <= numTables) x 16.
115: * USHORT entrySelector Log2(maximum power of 2 <= numTables).
116: * USHORT rangeShift NumTables x 16-searchRange.
117: * </pre>
118: *
119: * @throws IOException
120: */
121: protected void writeFontHeader() throws IOException {
122: int numTables = getFont().getTables().length;
123: int maxPower = 1;
124: int log2MaxPower = 0;
125: while (maxPower <= numTables) {
126: maxPower = maxPower << 1;
127: log2MaxPower += 1;
128: }
129: if (log2MaxPower > 0) {
130: // we are 1 step to far...
131: maxPower = maxPower >> 1;
132: log2MaxPower--;
133: }
134: writeInt(getOutputStream(), 0x00010000);
135: writeShort(getOutputStream(), numTables);
136: writeShort(getOutputStream(), maxPower << 4);
137: writeShort(getOutputStream(), log2MaxPower);
138: writeShort(getOutputStream(), (numTables - maxPower) << 4);
139: }
140:
141: protected void writeInt(OutputStream os, int value)
142: throws IOException {
143: os.write((byte) (value >> 24));
144: os.write((byte) (value >> 16));
145: os.write((byte) (value >> 8));
146: os.write((byte) (value));
147: }
148:
149: protected void writeShort(OutputStream os, int value)
150: throws IOException {
151: os.write((byte) (value >> 8));
152: os.write((byte) (value));
153: }
154:
155: protected void writeTableDirectory() throws IOException {
156: // todo recompute table directory information
157: int tableCount = getFont().getTables().length;
158: tableOffset = (tableCount * 16) + 12;
159: for (int i = 0; i < tableCount; i++) {
160: TTTable table = getFont().getTables()[i];
161: writeTableDirectoryTable(table);
162: }
163: }
164:
165: protected void writeTableDirectoryTable(TTTable table)
166: throws IOException {
167: int length = (int) table.getLength();
168: writeBytes(getOutputStream(), table.getName(), 0, table
169: .getName().length);
170: writeInt(getOutputStream(), (int) table.getChecksum());
171: writeInt(getOutputStream(), (int) tableOffset);
172: writeInt(getOutputStream(), length);
173: // table.setOffset(tableOffset);
174: tableOffset = (tableOffset + length + 3) & (~3);
175: }
176:
177: protected void writeTables() throws IOException {
178: int tableCount = getFont().getTables().length;
179:
180: /**
181: * java.util.Arrays.sort(getFont().getTables(), new Comparator() {
182: * public int compare(Object o1, Object o2) { return (int) (((TTTable)
183: * o1).getOffset() - ((TTTable) o2).getOffset()); }});
184: */
185: for (int i = 0; i < tableCount; i++) {
186: TTTable table = getFont().getTables()[i];
187: writeTablesTable(table);
188: }
189: }
190:
191: protected void writeTablesTable(TTTable table) throws IOException {
192: IRandomAccess random = table.getRandomAccess();
193: try {
194: random.seek(0);
195: int length = (int) table.getLength();
196: for (int i = 0; i < length; i++) {
197: writeByte(getOutputStream(), random.read());
198: }
199: // pad with zeros
200: // todo make faster
201: for (int i = length; i < ((length + 3) & (~3)); i++) {
202: writeByte(getOutputStream(), (byte) 0);
203: }
204: } finally {
205: StreamTools.close(random);
206: }
207: }
208: }
|