diff options
author | Bernhard Haumacher <[email protected]> | 2020-05-10 20:46:02 +0200 |
---|---|---|
committer | Sven Göthel <[email protected]> | 2024-02-03 02:00:53 +0100 |
commit | adc90be6effd090f217e2613d0dab13f9a1ad7c9 (patch) | |
tree | 4ceb76c4e271b62a2682c95826292ed633568e83 | |
parent | 2a23fede0f8ee99ed958f0f547f2558da7662cd9 (diff) |
Added support for reading the SVG table. # Conflicts: # src/jogl/classes/jogamp/graph/font/typecast/ot/TTFont.java # src/jogl/classes/jogamp/graph/font/typecast/ot/table/Table.java
4 files changed, 363 insertions, 48 deletions
diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/TTFont.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/TTFont.java index 90ddda48a..ee3e46000 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/TTFont.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/TTFont.java @@ -32,6 +32,7 @@ import jogamp.graph.font.typecast.ot.table.GlyfTable; import jogamp.graph.font.typecast.ot.table.HdmxTable; import jogamp.graph.font.typecast.ot.table.KernTable; import jogamp.graph.font.typecast.ot.table.LocaTable; +import jogamp.graph.font.typecast.ot.table.SVGTable; import jogamp.graph.font.typecast.ot.table.Table; import jogamp.graph.font.typecast.ot.table.VdmxTable; @@ -42,6 +43,7 @@ public class TTFont extends OTFont { private KernTable _kern; private HdmxTable _hdmx; private VdmxTable _vdmx; + private SVGTable _svg; private static TableDirectory readTableDir(final DataInputStream dis, final int directoryOffset) throws IOException { // Load the table directory @@ -120,12 +122,19 @@ public class TTFont extends OTFont { // 'loca' is required by 'glyf' int length = seekTable(dis, tablesOrigin, Table.loca); - 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) - length = seekTable(dis, tablesOrigin, Table.glyf); - _glyf = new GlyfTable(dis, length, this.getMaxpTable(), loca); + if (length > 0) { + 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) + length = seekTable(dis, tablesOrigin, Table.glyf); + _glyf = new GlyfTable(dis, length, this.getMaxpTable(), loca); + } + + length = seekTable(dis, tablesOrigin, Table.svg); + if (length > 0) { + _svg = new SVGTable(dis); + } length = seekTable(dis, tablesOrigin, Table.gasp); if (length > 0) { @@ -151,6 +160,13 @@ public class TTFont extends OTFont { public GlyfTable getGlyfTable() { return _glyf; } + + /** + * Optional {@link SVGTable}. + */ + public SVGTable getSvgTable() { + return _svg; + } public GaspTable getGaspTable() { return _gasp; @@ -188,6 +204,7 @@ public class TTFont extends OTFont { public void dumpTo(Writer out) throws IOException { super.dumpTo(out); dump(out, getGlyfTable()); + dump(out, getSvgTable()); dump(out, getGaspTable()); dump(out, getKernTable()); dump(out, getHdmxTable()); diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/SVGTable.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/SVGTable.java new file mode 100644 index 000000000..c85661dc2 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/SVGTable.java @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2020 Business Operation Systems GmbH. All Rights Reserved. + */ +package jogamp.graph.font.typecast.ot.table; + +import java.io.ByteArrayInputStream; +import java.io.DataInput; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Writer; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.zip.GZIPInputStream; + +/** + * The SVG (Scalable Vector Graphics) table + * + * <p> + * This table contains SVG descriptions for some or all of the glyphs in the font. + * </p> + * + * @see "https://docs.microsoft.com/en-us/typography/opentype/spec/svg" + * + * @author <a href="mailto:[email protected]">Bernhard Haumacher</a> + */ +public class SVGTable implements Table { + + /** + * @see #getVersion() + */ + public static final int VERSION = 0; + + /** + * @see #getVersion() + */ + private int _version = VERSION; + + /** + * @see #getDocumentRecords() + */ + private SVGDocumentRecord[] _documentRecords; + + /** + * Creates a {@link SVGTable}. + * + * @param di The reader to read from. + */ + public SVGTable(DataInput di) throws IOException { + _version = di.readUnsignedShort(); + + // Offset to the SVG Document List, from the start of the SVG table. + // Must be non-zero. + int offsetToSVGDocumentList = di.readInt(); + di.skipBytes(offsetToSVGDocumentList - 6); + + // SVG Document List starts here. + int offset = 0; + + // uint16 Number of SVG document records. Must be non-zero. + int numEntries = di.readUnsignedShort(); + offset += 2; + + _documentRecords = new SVGDocumentRecord[numEntries]; + for (int n = 0; n < numEntries; n++) { + _documentRecords[n] = new SVGDocumentRecord(di); + } + offset += numEntries * 12; + + SVGDocumentRecord[] recordsInOffsetOrder = new SVGDocumentRecord[numEntries]; + System.arraycopy(_documentRecords, 0, recordsInOffsetOrder, 0, numEntries); + Arrays.sort(recordsInOffsetOrder, (a, b) -> Integer.compare(a.getSvgDocOffset(), b.getSvgDocOffset())); + + int lastOffset = 0; + for (int n = 0; n < numEntries; n++) { + SVGDocumentRecord record = recordsInOffsetOrder[n]; + + int docOffset = record.getSvgDocOffset(); + if (docOffset == lastOffset) { + // Pointing to the same document. + record.setDocument(recordsInOffsetOrder[n - 1].getDocument()); + } else { + int skip = docOffset - offset; + assert skip >= 0; + di.skipBytes(skip); + offset = docOffset; + } + lastOffset = offset; + record.readDoc(di); + offset += record.getSvgDocLength(); + } + } + + /** + * Records must be sorted in order of increasing startGlyphID. For any given + * record, the startGlyphID must be less than or equal to the endGlyphID of + * that record, and also must be greater than the endGlyphID of any previous + * record. + * + * <p> + * Note: Two or more records can point to the same SVG document. In this + * way, a single SVG document can provide glyph descriptions for + * discontinuous glyph ID ranges. + * </p> + */ + public SVGDocumentRecord[] getDocumentRecords() { + return _documentRecords; + } + + /** + * uint16 version Table version (starting at 0). Set to {@link #VERSION}. + */ + public int getVersion() { + return _version; + } + + @Override + public int getType() { + return 0; + } + + @Override + public String toString() { + return "SVG Table\n" + + "---------\n" + + " Version: " + getVersion() + "\n" + + " Number of records: " + getDocumentRecords().length; + } + + @Override + public void dump(Writer out) throws IOException { + Table.super.dump(out); + + for (SVGDocumentRecord record : getDocumentRecords()) { + out.write("\n\n"); + out.write(record.toString()); + } + } + + /** + * Each SVG document record specifies a range of glyph IDs (from + * startGlyphID to endGlyphID, inclusive), and the location of its + * associated SVG document in the SVG table. + */ + public static class SVGDocumentRecord { + + /** + * @see #getStartGlyphID() + */ + private int _startGlyphID; + + /** + * @see #getEndGlyphID() + */ + private int _endGlyphID; + + /** + * @see #getSvgDocOffset() + */ + private int _svgDocOffset; + + /** + * @see #getSvgDocLength() + */ + private int _svgDocLength; + + private String _document; + + /** + * Creates a {@link SVGDocumentRecord}. + * + * @param di + */ + public SVGDocumentRecord(DataInput di) throws IOException { + _startGlyphID = di.readUnsignedShort(); + _endGlyphID = di.readUnsignedShort(); + _svgDocOffset = di.readInt(); + _svgDocLength = di.readInt(); + } + + /** + * Reads the SVG document from the given reader. + */ + public void readDoc(DataInput di) throws IOException { + byte[] docData = new byte[getSvgDocLength()]; + di.readFully(docData); + + if (docData[0] == 0x1F && docData[1] == 0x8B && docData[2] == 0x08) { + // Gzip encoded document. + try (GZIPInputStream in = new GZIPInputStream(new ByteArrayInputStream(docData))) { + readDoc(in); + } + } else { + try (InputStream in = new ByteArrayInputStream(docData)) { + readDoc(in); + } + } + } + + private void readDoc(InputStream in) throws IOException { + StringBuilder doc = new StringBuilder(); + char[] buffer = new char[4096]; + try (InputStreamReader r = new InputStreamReader(in, Charset.forName("utf-8"))) { + while (true) { + int direct = r.read(buffer); + if (direct < 0) { + break; + } + doc.append(buffer, 0, direct); + } + } + + _document = doc.toString(); + } + + /** + * uint16 + * + * The first glyph ID for the range covered by this record. + */ + public int getStartGlyphID() { + return _startGlyphID; + } + + /** + * uint16 + * + * The last glyph ID for the range covered by this record. + */ + public int getEndGlyphID() { + return _endGlyphID; + } + + /** + * Offset32 + * + * Offset from the beginning of the SVGDocumentList to an SVG document. Must be non-zero. + */ + public int getSvgDocOffset() { + return _svgDocOffset; + } + + /** + * uint32 + * + * Length of the SVG document data. Must be non-zero. + */ + public int getSvgDocLength() { + return _svgDocLength; + } + + /** + * The SVG document as XML string. + */ + public String getDocument() { + return _document; + } + + /** + * @see #getDocument() + */ + public void setDocument(String document) { + _document = document; + } + + @Override + public String toString() { + return + " SVG document record\n" + + " -------------------\n" + + " startGlyphID: " + getStartGlyphID() + "\n" + + " endGlyphID: " + getEndGlyphID() + "\n" + + " svg: " + getDocument(); + } + } +} diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/Table.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/Table.java index 837411d83..499fa7492 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/Table.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/Table.java @@ -26,44 +26,45 @@ import java.io.Writer; public interface Table { // Table constants - static final int BASE = 0x42415345; // Baseline data [OpenType] - static final int CFF = 0x43464620; // PostScript font program (compact font format) [PostScript] - static final int COLR = 0x434f4c52; // Color Table - static final int CPAL = 0x4350414c; // Color Palette Table - static final int DSIG = 0x44534947; // Digital signature - static final int EBDT = 0x45424454; // Embedded bitmap data - static final int EBLC = 0x45424c43; // Embedded bitmap location data - static final int EBSC = 0x45425343; // Embedded bitmap scaling data - static final int GDEF = 0x47444546; // Glyph definition data [OpenType] - static final int GPOS = 0x47504f53; // Glyph positioning data [OpenType] - static final int GSUB = 0x47535542; // Glyph substitution data [OpenType] - static final int JSTF = 0x4a535446; // Justification data [OpenType] - static final int LTSH = 0x4c545348; // Linear threshold table - static final int MMFX = 0x4d4d4658; // Multiple master font metrics [PostScript] - static final int MMSD = 0x4d4d5344; // Multiple master supplementary data [PostScript] - static final int OS_2 = 0x4f532f32; // OS/2 and Windows specific metrics [r] - static final int PCLT = 0x50434c54; // PCL5 - static final int VDMX = 0x56444d58; // Vertical Device Metrics table - static final int cmap = 0x636d6170; // character to glyph mapping [r] - static final int cvt = 0x63767420; // Control Value Table - static final int fpgm = 0x6670676d; // font program - static final int fvar = 0x66766172; // Apple's font variations table [PostScript] - static final int gasp = 0x67617370; // grid-fitting and scan conversion procedure (grayscale) - static final int glyf = 0x676c7966; // glyph data [r] - static final int hdmx = 0x68646d78; // horizontal device metrics - static final int head = 0x68656164; // font header [r] - static final int hhea = 0x68686561; // horizontal header [r] - static final int hmtx = 0x686d7478; // horizontal metrics [r] - static final int kern = 0x6b65726e; // kerning - static final int loca = 0x6c6f6361; // index to location [r] - static final int maxp = 0x6d617870; // maximum profile [r] - static final int name = 0x6e616d65; // naming table [r] - static final int prep = 0x70726570; // CVT Program - static final int post = 0x706f7374; // PostScript information [r] - static final int sbix = 0x73626978; // Extended Bitmaps - static final int vhea = 0x76686561; // Vertical Metrics header - static final int vmtx = 0x766d7478; // Vertical Metrics - + int BASE = 0x42415345; // Baseline data [OpenType] + int CFF = 0x43464620; // PostScript font program (compact font format) [PostScript] + int COLR = 0x434f4c52; // Color Table + int CPAL = 0x4350414c; // Color Palette Table + int DSIG = 0x44534947; // Digital signature + int EBDT = 0x45424454; // Embedded bitmap data + int EBLC = 0x45424c43; // Embedded bitmap location data + int EBSC = 0x45425343; // Embedded bitmap scaling data + int GDEF = 0x47444546; // Glyph definition data [OpenType] + int GPOS = 0x47504f53; // Glyph positioning data [OpenType] + int GSUB = 0x47535542; // Glyph substitution data [OpenType] + int JSTF = 0x4a535446; // Justification data [OpenType] + int LTSH = 0x4c545348; // Linear threshold table + int MMFX = 0x4d4d4658; // Multiple master font metrics [PostScript] + int MMSD = 0x4d4d5344; // Multiple master supplementary data [PostScript] + int OS_2 = 0x4f532f32; // OS/2 and Windows specific metrics [r] + int PCLT = 0x50434c54; // PCL5 + int VDMX = 0x56444d58; // Vertical Device Metrics table + int cmap = 0x636d6170; // character to glyph mapping [r] + int cvt = 0x63767420; // Control Value Table + int fpgm = 0x6670676d; // font program + int fvar = 0x66766172; // Apple's font variations table [PostScript] + int gasp = 0x67617370; // grid-fitting and scan conversion procedure (grayscale) + int glyf = 0x676c7966; // glyph data [r] + int hdmx = 0x68646d78; // horizontal device metrics + int head = 0x68656164; // font header [r] + int hhea = 0x68686561; // horizontal header [r] + int hmtx = 0x686d7478; // horizontal metrics [r] + int kern = 0x6b65726e; // kerning + int loca = 0x6c6f6361; // index to location [r] + int maxp = 0x6d617870; // maximum profile [r] + int name = 0x6e616d65; // naming table [r] + int prep = 0x70726570; // CVT Program + int post = 0x706f7374; // PostScript information [r] + int sbix = 0x73626978; // Extended Bitmaps + int vhea = 0x76686561; // Vertical Metrics header + int vmtx = 0x766d7478; // Vertical Metrics + int svg = TableDirectory.fromStringTag("SVG "); // SVG outlines + /** * The type code of this {@link Table}. */ diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/TableDirectory.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/TableDirectory.java index cd4b0f462..a6ea4df31 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/TableDirectory.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/TableDirectory.java @@ -92,10 +92,8 @@ public class TableDirectory { } String getTagAsString() { - return String.valueOf((char) ((_tag >> 24) & 0xff)) + - (char) ((_tag >> 16) & 0xff) + - (char) ((_tag >> 8) & 0xff) + - (char) ((_tag) & 0xff); + int tag = _tag; + return TableDirectory.toStringTag(tag); } @Override @@ -174,4 +172,27 @@ public class TableDirectory { } return sb.toString(); } + + /** + * Converts a {@link Table} tag back to {@link String}. + */ + public static String toStringTag(int tag) { + return String.valueOf( + (char) ((tag >> 24) & 0xff)) + + (char) ((tag >> 16) & 0xff) + + (char) ((tag >> 8) & 0xff) + + (char) (tag & 0xff); + } + + /** + * Converts a {@link Table} tag {@link String} to an ID. + */ + public static int fromStringTag(String tag) { + assert tag.length() == 4; + return + ((tag.charAt(0) & 0xFF) << 24) | + ((tag.charAt(1) & 0xFF) << 16) | + ((tag.charAt(2) & 0xFF) << 8) | + (tag.charAt(3) & 0xff); + } } |