aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2023-02-12 00:08:39 +0100
committerSven Gothel <[email protected]>2023-02-12 00:22:44 +0100
commit990c613770dbc0ef81bdc448f159eb1dd66c87ec (patch)
tree1f08a7de45b13e163f3364d1381a6d224e671f96
parent093b4b2bc90595dc65c32e65bba1e1909e8d5b18 (diff)
*Font*: Bring back loading fonts and glyph directly via input stream w/o font data array copies and re-wrapped input-stream
Adjusted TTFontTest and SVGExporterTest using a direct URLConnection's input-stream for a single TTFont.
-rw-r--r--src/main/java/net/java/dev/typecast/ot/OTFont.java72
-rw-r--r--src/main/java/net/java/dev/typecast/ot/OTFontCollection.java110
-rw-r--r--src/main/java/net/java/dev/typecast/ot/TTFont.java95
-rw-r--r--src/main/java/net/java/dev/typecast/ot/table/TableDirectory.java17
-rw-r--r--src/test/java/net/java/dev/typecast/exchange/SVGExporterTest.java13
-rw-r--r--src/test/java/net/java/dev/typecast/ot/TTFontTest.java13
6 files changed, 225 insertions, 95 deletions
diff --git a/src/main/java/net/java/dev/typecast/ot/OTFont.java b/src/main/java/net/java/dev/typecast/ot/OTFont.java
index 086db52..645ddcb 100644
--- a/src/main/java/net/java/dev/typecast/ot/OTFont.java
+++ b/src/main/java/net/java/dev/typecast/ot/OTFont.java
@@ -22,7 +22,23 @@ import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
-import net.java.dev.typecast.ot.table.*;
+import net.java.dev.typecast.ot.table.CmapTable;
+import net.java.dev.typecast.ot.table.GsubTable;
+import net.java.dev.typecast.ot.table.HdmxTable;
+import net.java.dev.typecast.ot.table.HeadTable;
+import net.java.dev.typecast.ot.table.HheaTable;
+import net.java.dev.typecast.ot.table.HmtxTable;
+import net.java.dev.typecast.ot.table.KernTable;
+import net.java.dev.typecast.ot.table.LocaTable;
+import net.java.dev.typecast.ot.table.MaxpTable;
+import net.java.dev.typecast.ot.table.NameRecord;
+import net.java.dev.typecast.ot.table.NameTable;
+import net.java.dev.typecast.ot.table.Os2Table;
+import net.java.dev.typecast.ot.table.PostTable;
+import net.java.dev.typecast.ot.table.Table;
+import net.java.dev.typecast.ot.table.TableDirectory;
+import net.java.dev.typecast.ot.table.VheaTable;
+
/**
* The TrueType font.
@@ -30,29 +46,6 @@ import net.java.dev.typecast.ot.table.*;
*/
public abstract class OTFont {
-
- /**
- * @param fontData OpenType/TrueType font file data.
- * @param directoryOffset The Table Directory offset within the file. For a
- * regular TTF/OTF file this will be zero, but for a TTC (Font Collection)
- * the offset is retrieved from the TTC header. For a Mac font resource,
- * offset is retrieved from the resource headers.
- * @param tablesOrigin The point the table offsets are calculated from.
- * Once again, in a regular TTF file, this will be zero. In a TTC is is
- * also zero, but within a Mac resource, it is the beginning of the
- * individual font resource data.
- * @throws java.io.IOException
- */
- OTFont(byte[] fontData, int tablesOrigin) throws IOException {
-
- // Load the table directory
-// dis.skip(directoryOffset);
- TableDirectory tableDirectory = new TableDirectory(fontData);
-
- DataInputStream dis = new DataInputStream(new ByteArrayInputStream(fontData));
- dis.mark(fontData.length);
- dis.reset();
-
private final Os2Table _os2;
private final CmapTable _cmap;
private final HeadTable _head;
@@ -63,6 +56,15 @@ public abstract class OTFont {
private final PostTable _post;
private final VheaTable _vhea;
private final GsubTable _gsub;
+
+ /**
+ *
+ * @param dis input stream marked at start with read-ahead set to known stream length
+ * @param tableDirectory
+ * @param tablesOrigin
+ * @throws IOException
+ */
+ OTFont(final DataInputStream dis, TableDirectory tableDirectory, final int tablesOrigin) throws IOException {
// Load some prerequisite tables
// (These are tables that are referenced by other tables, so we need to load
// them first)
@@ -81,6 +83,8 @@ public abstract class OTFont {
int length = seekTable(tableDirectory, dis, tablesOrigin, Table.vhea);
if (length > 0) {
_vhea = new VheaTable(dis);
+ } else {
+ _vhea = null;
}
// 'post' is required by 'glyf'
@@ -96,24 +100,26 @@ public abstract class OTFont {
_name = new NameTable(dis, length);
seekTable(tableDirectory, dis, tablesOrigin, Table.OS_2);
_os2 = new Os2Table(dis);
+
+ _gsub = null; // FIXME: delete?
}
public Os2Table getOS2Table() {
return _os2;
}
-
+
public CmapTable getCmapTable() {
return _cmap;
}
-
+
public HeadTable getHeadTable() {
return _head;
}
-
+
public HheaTable getHheaTable() {
return _hhea;
}
-
+
public HmtxTable getHmtxTable() {
return _hmtx;
}
@@ -153,12 +159,12 @@ public abstract class OTFont {
public abstract Glyph getGlyph(int i);
int seekTable(
- TableDirectory tableDirectory,
- DataInputStream dis,
- int tablesOrigin,
- int tag) throws IOException {
+ final TableDirectory tableDirectory,
+ final DataInputStream dis,
+ final int tablesOrigin,
+ final int tag) throws IOException {
dis.reset();
- TableDirectory.Entry entry = tableDirectory.getEntryByTag(tag);
+ final TableDirectory.Entry entry = tableDirectory.getEntryByTag(tag);
if (entry == null) {
return 0;
}
diff --git a/src/main/java/net/java/dev/typecast/ot/OTFontCollection.java b/src/main/java/net/java/dev/typecast/ot/OTFontCollection.java
index bee9b70..f61dd33 100644
--- a/src/main/java/net/java/dev/typecast/ot/OTFontCollection.java
+++ b/src/main/java/net/java/dev/typecast/ot/OTFontCollection.java
@@ -23,7 +23,7 @@ import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
-import java.nio.file.Files;
+import java.io.InputStream;
import net.java.dev.typecast.ot.mac.ResourceHeader;
import net.java.dev.typecast.ot.mac.ResourceMap;
import net.java.dev.typecast.ot.mac.ResourceReference;
@@ -40,16 +40,26 @@ class OTFontCollection {
private final boolean DEBUG = false;
private TTCHeader _ttcHeader;
private OTFont[] _fonts;
+ private String _pathName;
+ private String _fileName;
+ private boolean _resourceFork = false;
+ public String getPathName() {
+ return _pathName;
+ }
+
+ public String getFileName() {
+ return _fileName;
+ }
- public OTFont getFont(int i) {
+ public OTFont getFont(final int i) {
return _fonts[i];
}
-
+
public int getFontCount() {
return _fonts.length;
}
-
+
public TTCHeader getTtcHeader() {
return _ttcHeader;
}
@@ -57,36 +67,83 @@ class OTFontCollection {
/**
* @param file The OpenType font file
*/
- public OTFontCollection(File file) throws IOException {
+ public OTFontCollection(final File file) throws IOException {
+ read(file);
+ }
+
+ /**
+ * @param istream The OpenType font input stream
+ * @param streamLen the length of the OpenType font segment in the stream
+ */
+ public OTFontCollection(final InputStream istream, final int streamLen) throws IOException {
+ read(istream, streamLen);
+ }
+
+ /**
+ * @param file The OpenType font file
+ */
+ protected void read(File file) throws IOException {
+ _pathName = file.getPath();
+ _fileName = file.getName();
+
if (!file.exists()) {
- throw new IOException();
+ throw new IOException("File <"+file.getName()+"> doesn't exist.");
}
// Do we need to modify the path name to deal with font resources
// in a Mac resource fork?
- boolean resourceFork = false;
if (file.length() == 0) {
file = new File(file, "..namedfork/rsrc");
if (!file.exists()) {
- throw new IOException();
+ throw new IOException("File <"+file.getName()+"> doesn't exist.");
}
- resourceFork = true;
+ _resourceFork = true;
+ }
+
+ final int streamLen = (int) file.length();
+ final BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file), streamLen);
+ try {
+ readImpl(bis, streamLen);
+ } finally {
+ bis.close();
}
+ }
- DataInputStream dis = new DataInputStream(
- new BufferedInputStream(
- new FileInputStream(file), (int) file.length()));
- dis.mark((int) file.length());
+ /**
+ * @param is The OpenType font stream
+ * @param streamLen the length of the OpenType font segment in the stream
+ */
+ protected void read(final InputStream is, final int streamLen) throws IOException {
+ _pathName = "";
+ _fileName = "";
+ final InputStream bis;
+ if( is.markSupported() ) {
+ bis = is;
+ } else {
+ bis = new BufferedInputStream(is, streamLen);
+ }
+ readImpl(bis, streamLen);
+ }
+
+ /**
+ * @param is The OpenType font stream, must {@link InputStream#markSupported() support mark}!
+ */
+ private void readImpl(final InputStream bis, final int streamLen) throws IOException {
+ if( !bis.markSupported() ) {
+ throw new IllegalArgumentException("stream of type "+bis.getClass().getName()+" doesn't support mark");
+ }
+ bis.mark(streamLen);
+ final DataInputStream dis = new DataInputStream(bis);
- if (resourceFork || file.getPath().endsWith(".dfont")) {
+ if (_resourceFork || _pathName.endsWith(".dfont")) {
// This is a Macintosh font suitcase resource
- ResourceHeader resourceHeader = new ResourceHeader(dis);
+ final ResourceHeader resourceHeader = new ResourceHeader(dis);
// Seek to the map offset and read the map
dis.reset();
dis.skip(resourceHeader.getMapOffset());
- ResourceMap map = new ResourceMap(dis);
+ final ResourceMap map = new ResourceMap(dis);
if( DEBUG ) {
// Dump some info about the font suitcase
@@ -102,16 +159,15 @@ class OTFontCollection {
}
// Get the 'sfnt' resources
- ResourceType resourceType = map.getResourceType("sfnt");
+ final ResourceType resourceType = map.getResourceType("sfnt");
// Load the font data
_fonts = new OTFont[resourceType.getCount()];
for (int i = 0; i < resourceType.getCount(); i++) {
- ResourceReference resourceReference = resourceType.getReference(i);
- int offset = resourceHeader.getDataOffset() +
- resourceReference.getDataOffset() + 4;
- byte[] fontData = Files.readAllBytes(file.toPath());
- _fonts[i] = new TTFont(fontData, offset /*, offset*/);
+ final ResourceReference resourceReference = resourceType.getReference(i);
+ final int offset = resourceHeader.getDataOffset() +
+ resourceReference.getDataOffset() + 4;
+ _fonts[i] = new TTFont(dis, offset, offset);
}
} else if (TTCHeader.isTTC(dis)) {
@@ -119,17 +175,15 @@ class OTFontCollection {
// This is a TrueType font collection
dis.reset();
_ttcHeader = new TTCHeader(dis);
- _fonts = new OTFont[_ttcHeader.getDirectoryCount()];
+ _fonts = new TTFont[_ttcHeader.getDirectoryCount()];
for (int i = 0; i < _ttcHeader.getDirectoryCount(); i++) {
- byte[] fontData = Files.readAllBytes(file.toPath());
- _fonts[i] = new TTFont(fontData, _ttcHeader.getTableDirectory(i));
+ _fonts[i] = new TTFont(dis, _ttcHeader.getTableDirectory(i), 0);
}
} else {
// This is a standalone font file
- _fonts = new OTFont[1];
- byte[] fontData = Files.readAllBytes(file.toPath());
- _fonts[0] = new TTFont(fontData, 0);
+ _fonts = new TTFont[1];
+ _fonts[0] = new TTFont(dis, 0, 0);
// TODO T2Fonts
}
diff --git a/src/main/java/net/java/dev/typecast/ot/TTFont.java b/src/main/java/net/java/dev/typecast/ot/TTFont.java
index 55f333c..4505f1b 100644
--- a/src/main/java/net/java/dev/typecast/ot/TTFont.java
+++ b/src/main/java/net/java/dev/typecast/ot/TTFont.java
@@ -18,40 +18,105 @@
package net.java.dev.typecast.ot;
-import net.java.dev.typecast.ot.table.*;
-
-import java.io.ByteArrayInputStream;
+import java.io.BufferedInputStream;
import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStream;
+
+import net.java.dev.typecast.ot.table.GaspTable;
+import net.java.dev.typecast.ot.table.GlyfDescript;
+import net.java.dev.typecast.ot.table.GlyfTable;
+import net.java.dev.typecast.ot.table.HdmxTable;
+import net.java.dev.typecast.ot.table.KernTable;
+import net.java.dev.typecast.ot.table.LocaTable;
+import net.java.dev.typecast.ot.table.Table;
+import net.java.dev.typecast.ot.table.TableDirectory;
+import net.java.dev.typecast.ot.table.VdmxTable;
public class TTFont extends OTFont {
- private GlyfTable _glyf;
+ private final GlyfTable _glyf;
private GaspTable _gasp;
private KernTable _kern;
private HdmxTable _hdmx;
private VdmxTable _vdmx;
+ private static TableDirectory readTableDir(final DataInputStream dis, final int directoryOffset) throws IOException {
+ // Load the table directory
+ dis.reset(); // throws if not marked or mark not supported
+ dis.skip(directoryOffset);
+ return new TableDirectory(dis);
+ }
+
+ private static DataInputStream openStream(final File file) throws IOException {
+ if (!file.exists()) {
+ throw new IOException("File <"+file.getName()+"> doesn't exist.");
+ }
+ final int streamLen = (int) file.length();
+ final BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file), streamLen);
+ if( !bis.markSupported() ) {
+ throw new IllegalArgumentException("stream of type "+bis.getClass().getName()+" doesn't support mark");
+ }
+ bis.mark(streamLen);
+ return new DataInputStream(bis);
+ }
+
+ private static DataInputStream openStream(final InputStream is, final int streamLen) throws IOException {
+ final BufferedInputStream bis = new BufferedInputStream(is, streamLen);
+ if( !bis.markSupported() ) {
+ throw new IllegalArgumentException("stream of type "+is.getClass().getName()+" doesn't support mark");
+ }
+ bis.mark(streamLen);
+ return new DataInputStream(bis);
+ }
+
/**
* Constructor
- *
- * @param fontData
+ * @param file standalone font file
* @param tablesOrigin
+ * @throws IOException
*/
- public TTFont(byte[] fontData, int tablesOrigin) throws IOException {
- super(fontData, tablesOrigin);
+ public TTFont(final File file) throws IOException {
+ this(openStream(file), 0, 0);
+ }
- // Load the table directory
-// dis.skip(directoryOffset);
- TableDirectory tableDirectory = new TableDirectory(fontData);
+ /**
+ * Constructor
+ * @param is standalone font input stream
+ * @param streamLen length of input stream to rewind across whole font data set
+ * @throws IOException
+ */
+ public TTFont(final InputStream is, final int streamLen) throws IOException {
+ this(openStream(is, streamLen), 0, 0);
+ }
+
+ /**
+ * Constructor
+ * @param dis input stream marked at start with read-ahead set to known stream length
+ * @param directoryOffset
+ * @param tablesOrigin
+ * @return
+ * @throws IOException
+ */
+ public TTFont(final DataInputStream dis, final int directoryOffset, final int tablesOrigin) throws IOException {
+ this(dis, readTableDir(dis, directoryOffset), tablesOrigin);
+ }
- DataInputStream dis = new DataInputStream(new ByteArrayInputStream(fontData));
- dis.mark(fontData.length);
- dis.reset();
+ /**
+ *
+ * @param dis input stream marked at start with read-ahead set to known stream length
+ * @param tableDirectory
+ * @param tablesOrigin
+ * @throws IOException
+ */
+ TTFont(final DataInputStream dis, final TableDirectory tableDirectory, final int tablesOrigin) throws IOException {
+ super(dis, tableDirectory, tablesOrigin);
// 'loca' is required by 'glyf'
int length = seekTable(tableDirectory, dis, tablesOrigin, Table.loca);
- LocaTable loca = new LocaTable(dis, length, this.getHeadTable(), this.getMaxpTable());
+ final LocaTable loca = new LocaTable(dis, length, this.getHeadTable(), this.getMaxpTable());
// If this is a TrueType outline, then we'll have at least the
// 'glyf' table (along with the 'loca' table)
diff --git a/src/main/java/net/java/dev/typecast/ot/table/TableDirectory.java b/src/main/java/net/java/dev/typecast/ot/table/TableDirectory.java
index 733a7e1..dbdf8fb 100644
--- a/src/main/java/net/java/dev/typecast/ot/table/TableDirectory.java
+++ b/src/main/java/net/java/dev/typecast/ot/table/TableDirectory.java
@@ -50,9 +50,7 @@
package net.java.dev.typecast.ot.table;
-import java.io.ByteArrayInputStream;
import java.io.DataInput;
-import java.io.DataInputStream;
import java.io.IOException;
import net.java.dev.typecast.ot.Fixed;
@@ -114,16 +112,15 @@ public class TableDirectory {
private final short _rangeShift;
private final Entry[] _entries;
- public TableDirectory(byte[] fontData) throws IOException {
- DataInputStream dis = new DataInputStream(new ByteArrayInputStream(fontData));
- _version = dis.readInt();
- _numTables = dis.readShort();
- _searchRange = dis.readShort();
- _entrySelector = dis.readShort();
- _rangeShift = dis.readShort();
+ public TableDirectory(final DataInput di) throws IOException {
+ _version = di.readInt();
+ _numTables = di.readShort();
+ _searchRange = di.readShort();
+ _entrySelector = di.readShort();
+ _rangeShift = di.readShort();
_entries = new Entry[_numTables];
for (int i = 0; i < _numTables; i++) {
- _entries[i] = new Entry(dis);
+ _entries[i] = new Entry(di);
}
}
diff --git a/src/test/java/net/java/dev/typecast/exchange/SVGExporterTest.java b/src/test/java/net/java/dev/typecast/exchange/SVGExporterTest.java
index 11c8d4a..e9c58c5 100644
--- a/src/test/java/net/java/dev/typecast/exchange/SVGExporterTest.java
+++ b/src/test/java/net/java/dev/typecast/exchange/SVGExporterTest.java
@@ -27,8 +27,10 @@ import net.java.dev.typecast.ot.table.TableException;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
+import java.net.URLConnection;
import java.nio.file.Files;
public class SVGExporterTest extends TestCase {
@@ -49,10 +51,13 @@ public class SVGExporterTest extends TestCase {
}
public void testExportFont() throws URISyntaxException, IOException, TableException {
- URL url = ClassLoader.getSystemResource("Lato-Regular.ttf");
- File file = new File(url.toURI());
- byte[] fontData = Files.readAllBytes(file.toPath());
- TTFont font = new TTFont(fontData, 0);
+ final URL url = ClassLoader.getSystemResource("Lato-Regular.ttf");
+ final URLConnection con = url.openConnection();
+ final int len = con.getContentLength();
+ TTFont font;
+ try(InputStream is = con.getInputStream()) {
+ font = new TTFont(is, len);
+ }
SVGExporter exporter = new SVGExporter(font, 32, 32, "abc", true, false);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
exporter.export(baos);
diff --git a/src/test/java/net/java/dev/typecast/ot/TTFontTest.java b/src/test/java/net/java/dev/typecast/ot/TTFontTest.java
index 03076b9..1eb72ba 100644
--- a/src/test/java/net/java/dev/typecast/ot/TTFontTest.java
+++ b/src/test/java/net/java/dev/typecast/ot/TTFontTest.java
@@ -21,6 +21,7 @@ package net.java.dev.typecast.ot;
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
+import java.net.URLConnection;
import java.nio.file.Files;
import junit.framework.Test;
@@ -46,10 +47,12 @@ public class TTFontTest extends TestCase {
}
public void testLoadFont() throws URISyntaxException, IOException {
- URL url = ClassLoader.getSystemResource("Lato-Regular.ttf");
- File file = new File(url.toURI());
- byte[] fontData = Files.readAllBytes(file.toPath());
- TTFont font = new TTFont(fontData, 0);
- assertEquals(HeadTable.class, font.getHeadTable().getClass());
+ final URL url = ClassLoader.getSystemResource("Lato-Regular.ttf");
+ final URLConnection con = url.openConnection();
+ final int len = con.getContentLength();
+ try(InputStream is = con.getInputStream()) {
+ TTFont font = new TTFont(is, len);
+ assertEquals(HeadTable.class, font.getHeadTable().getClass());
+ }
}
}