static byte[] get_TTF_digest( OTFile f ) { OTFont fn = f.GetFont(0); Table_DSIG tDSIG = (Table_DSIG) fn.GetTable("DSIG"); DirectoryEntry deDSIG = null; // sort table by offset Dictionary<uint, int> offsetlookup = new Dictionary<uint, int>(); var list = new List<uint>(); for (ushort i=0; i<fn.GetNumTables(); i++) { DirectoryEntry de = fn.GetDirectoryEntry(i); offsetlookup.Add(de.offset, i); list.Add( de.offset ); if ((string) de.tag == "DSIG" ) deDSIG = de; } list.Sort(); // New offset table var old_ot = fn.GetOffsetTable(); OffsetTable ot = new OffsetTable (old_ot.sfntVersion, (ushort) ( old_ot.numTables - 1) ); for (ushort i=0; i<fn.GetNumTables(); i++) { DirectoryEntry oldde = fn.GetDirectoryEntry(i); if ( (string) oldde.tag != "DSIG" ) { DirectoryEntry de = new DirectoryEntry(oldde); de.offset -=16; // one less entry if ( de.offset > deDSIG.offset ) de.offset -= tDSIG.GetBuffer().GetPaddedLength(); ot.DirectoryEntries.Add(de); } } hash.TransformBlock (ot.m_buf.GetBuffer(), 0, (int)ot.m_buf.GetLength(), ot.m_buf.GetBuffer(), 0); for (int i=0; i< ot.DirectoryEntries.Count ; i++) { DirectoryEntry de = (DirectoryEntry)ot.DirectoryEntries[i]; hash.TransformBlock (de.m_buf.GetBuffer(), 0, (int)de.m_buf.GetLength(), de.m_buf.GetBuffer(), 0); } Table_head headTable = (Table_head) fn.GetTable("head"); // calculate the checksum uint sum = 0; sum += ot.CalcOffsetTableChecksum(); sum += ot.CalcDirectoryEntriesChecksum(); foreach (var key in list) { OTTable table = fn.GetTable((ushort)offsetlookup[key]); if ( (string) table.GetTag() != "DSIG") sum += table.CalcChecksum(); } Table_head.head_cache headCache = (Table_head.head_cache)headTable.GetCache(); // set the checkSumAdujustment field headCache.checkSumAdjustment = 0xb1b0afba - sum; Table_head newHead = (Table_head)headCache.GenerateTable(); foreach (var key in list) { OTTable table = fn.GetTable((ushort)offsetlookup[key]); if ( (string) table.GetTag() == "head" ) table = newHead; if ( (string) table.GetTag() != "DSIG" ) { hash.TransformBlock (table.m_bufTable.GetBuffer(), 0, (int)table.GetBuffer().GetPaddedLength(), table.m_bufTable.GetBuffer(), 0); } } byte[] usFlag = {0, 1}; hash.TransformFinalBlock(usFlag, 0,2); return hash.Hash; }
static int Main( string[] args ) { if (args.Length == 0) { Console.WriteLine("DSIGInfo [-v] [-v] [-v] fontfile"); return 0; } OTFile f = new OTFile(); Table_DSIG tDSIG = null; string filename = null; verbose = 0; for ( int i = 0; i < args.Length; i++ ) { if ( "-v" == args[i] ) verbose++; else filename = args[i]; } if ( !f.open(filename) ) { Console.WriteLine("Error: Cannot open {0} as font file", filename); return 0; } TTCHeader ttc = null; if ( f.IsCollection() ) { ttc = f.GetTTCHeader(); if ( f.GetTableManager().GetUnaliasedTableName(ttc.DsigTag) == "DSIG" ) { MBOBuffer buf = f.ReadPaddedBuffer(ttc.DsigOffset, ttc.DsigLength); tDSIG = (Table_DSIG) f.GetTableManager().CreateTableObject(ttc.DsigTag, buf); } for (uint i = 0; i < f.GetNumFonts() ; i++) { OTFont fn = f.GetFont(i); Table_DSIG memDSIG = (Table_DSIG) fn.GetTable("DSIG"); if (memDSIG != null) { Console.WriteLine("Warning: DSIG in member font"); break; } } } else { OTFont fn = f.GetFont(0); tDSIG = (Table_DSIG) fn.GetTable("DSIG"); } Console.WriteLine("{0} DSIG table: {1}", filename, ( tDSIG == null ) ? "Absent" : "Present" ); if (tDSIG == null) return 0; if ( f.IsCollection() && ttc.version != 0x00020000 ) Console.WriteLine("Warning: TTC has DSIG but header version is 0x{0}, != 0x00020000", ttc.version.ToString("X8")); if ( tDSIG.usNumSigs != 1 ) Console.WriteLine("NumSigs = {0}", tDSIG.usNumSigs); for ( uint v = 0; v < tDSIG.usNumSigs ; v++ ) { Table_DSIG.SignatureBlock sgb; try { sgb = tDSIG.GetSignatureBlock(v); } catch (IndexOutOfRangeException) { Console.WriteLine("Error: Out of Range SignatureBlock {0}", v); break; } SignedCms cms = new SignedCms(); cms.Decode(sgb.bSignature); if ( cms.SignerInfos.Count > 1 ) Console.WriteLine( "#SignerInfos: {0}", cms.SignerInfos.Count ); foreach ( var si in cms.SignerInfos ) { Console.WriteLine(si.Certificate); if ( Type.GetType("Mono.Runtime") == null ) foreach ( var ua in si.UnsignedAttributes ) { foreach ( var asnd in ua.Values ) { try { ASN1 vv = new ASN1(asnd.RawData); ASN1 t = new ASN1(vv[3][1][1].Value); Console.WriteLine("Decoded Signing Time: {0}", ASN1Convert.ToDateTime (t) ); } catch (Exception e) {/* Nothing to do */ } } } } Console.WriteLine( "#Certificates: {0}", cms.Certificates.Count ); #if HAVE_MONO_X509 certs = new Mono.Security.X509.X509CertificateCollection (); //Mono.Security.X509.X509Chain signerChain = new Mono.Security.X509.X509Chain (); #endif foreach ( var x509 in cms.Certificates ) { #if HAVE_MONO_X509 certs.Add(new Mono.Security.X509.X509Certificate(x509.RawData)); #endif if ( verbose > 0 ) { Console.WriteLine(x509); } else { Console.WriteLine(x509.Subject); } }; #if HAVE_MONO_X509 Mono.Security.X509.X509Certificate x = new Mono.Security.X509.X509Certificate(cms.SignerInfos[0].Certificate.RawData); Mono.Security.X509.X509Certificate parent = x; while (x != null) { // Self-signed is fine - the font bundled CA is self-signed. parent = x; // last valid x = FindCertificateParent (x); if (x != null && x.Equals(parent)) break; } #endif ASN1 spc = new ASN1(cms.ContentInfo.Content); ASN1 playload_oid = null; ASN1 oid = null; ASN1 digest = null; ASN1 obsolete = null; if ( Type.GetType("Mono.Runtime") == null ) { // DotNet is much saner! playload_oid = spc[0][0]; obsolete = spc[0][1][0]; oid = spc[1][0][0]; digest = spc[1][1]; } else { playload_oid = spc[0]; obsolete = spc[1][0]; oid = spc[2][0][0]; digest = spc[2][1]; } string algo = ASN1Convert.ToOid (oid); string algoname = (new Oid(algo)).FriendlyName; Console.WriteLine("Digest Algorithm: {0}", algoname); byte[] Value = digest.Value; StringBuilder hexLine_sig = new StringBuilder (); for (int i = 0; i < Value.Length; i++) { hexLine_sig.AppendFormat ("{0} ", Value [i].ToString ("X2")); } hexLine_sig.AppendFormat (Environment.NewLine); switch ( algoname ) { case "md5": hash = HashAlgorithm.Create ("MD5"); break; case "sha1": hash = HashAlgorithm.Create ("SHA1"); break; default: throw new NotImplementedException("Unknown HashAlgorithm: " + algoname ); } byte[] cdigest; if ( f.IsCollection() ) { cdigest = get_TTC_digest( f ); } else { cdigest = get_TTF_digest( f ); } StringBuilder hexLine = new StringBuilder (); for (int i = 0; i < cdigest.Length; i++) { hexLine.AppendFormat ("{0} ", cdigest [i].ToString ("X2")); } hexLine.AppendFormat (Environment.NewLine); Console.WriteLine("{0} Signed Digest:\t{1}", algoname.ToUpper(), hexLine_sig); Console.WriteLine("Calculated Digest:\t{0}", hexLine); string root_thumb = ""; #if HAVE_MONO_X509 root_thumb = (new System.Security.Cryptography.X509Certificates.X509Certificate2(parent.RawData)).Thumbprint; Console.WriteLine("ChainEnd Name: {0}", parent.SubjectName); Console.WriteLine("ChainEnd Self-Signed: {0}", parent.IsSelfSigned); #endif Console.WriteLine("ChainEnd: {0}", root_thumb); bool trusted = false; try { string root_id = trusted_roots[root_thumb]; Console.WriteLine("RootID: {0}", root_id); trusted = true; } catch (KeyNotFoundException) {} Console.WriteLine("Trusted: {0}", trusted); } return 0; }
static byte[] get_TTC_digest( OTFile f ) { TTCHeader ttc = f.GetTTCHeader(); uint back_shift = 12; //if ( ttc.version == 0x00020000 ) back_shift = 0; TTCHBuffer ttcb = new TTCHBuffer ( 12 + ttc.DirectoryCount * 4 + 12 - back_shift ); // write the TTC header ttcb.FillUint32MBO((uint) ttc.TTCTag); ttcb.FillUint32MBO(ttc.version); ttcb.FillUint32MBO(ttc.DirectoryCount); for (int i=0; i< f.GetNumFonts() ; i++) { ttcb.FillUint32MBO( (uint) ttc.DirectoryOffsets[i] - back_shift); } //if ( ttc.version == 0x00020000 ) { ttcb.FillUint32MBO( 0 ); ttcb.FillUint32MBO( 0 ); ttcb.FillUint32MBO( 0 ); } hash.TransformBlock ( ttcb.buffer, 0, ttcb.buffer.Length, ttcb.buffer, 0 ); // build an array of offset tables OffsetTable[] otArr = new OffsetTable[f.GetNumFonts()]; for (uint iFont=0; iFont<f.GetNumFonts(); iFont++) { otArr[iFont] = new OffsetTable(new OTFixed(1,0), f.GetFont(iFont).GetNumTables()); for (ushort i=0; i<f.GetFont(iFont).GetNumTables(); i++) { DirectoryEntry old = f.GetFont(iFont).GetDirectoryEntry(i); DirectoryEntry de = new DirectoryEntry(old); de.offset -= back_shift; otArr[iFont].DirectoryEntries.Add(de); } } if ( (uint) ttc.DirectoryOffsets[(int)f.GetNumFonts()-1] < f.GetFont(0).GetDirectoryEntry(0).offset ) { // assume directory-contiguous for (uint iFont=0; iFont<f.GetNumFonts(); iFont++) { // write the offset table hash.TransformBlock (otArr[iFont].m_buf.GetBuffer(), 0, (int)otArr[iFont].m_buf.GetLength(), otArr[iFont].m_buf.GetBuffer(), 0); // write the directory entries for (int i=0; i<f.GetFont(iFont).GetNumTables(); i++) { DirectoryEntry de = (DirectoryEntry)otArr[iFont].DirectoryEntries[i]; hash.TransformBlock (de.m_buf.GetBuffer(), 0, (int)de.m_buf.GetLength(), de.m_buf.GetBuffer(), 0 ); } } } // write out each font uint PrevPos = 0; for (uint iFont=0; iFont<f.GetNumFonts(); iFont++) { ushort numTables = f.GetFont(iFont).GetNumTables(); if ( (uint) ttc.DirectoryOffsets[(int)f.GetNumFonts()-1] > f.GetFont(0).GetDirectoryEntry(0).offset ) { // assume font-contiguous // write the offset table hash.TransformBlock (otArr[iFont].m_buf.GetBuffer(), 0, (int)otArr[iFont].m_buf.GetLength(), otArr[iFont].m_buf.GetBuffer(), 0 ); // write the directory entries for (int i=0; i<numTables; i++) { DirectoryEntry de = (DirectoryEntry)otArr[iFont].DirectoryEntries[i]; hash.TransformBlock (de.m_buf.GetBuffer(), 0, (int)de.m_buf.GetLength(), de.m_buf.GetBuffer(), 0 ); } } // write out each table unless a shared version has been written for (ushort i=0; i<numTables; i++) { DirectoryEntry de = (DirectoryEntry)otArr[iFont].DirectoryEntries[i]; if (PrevPos < de.offset) //crude { OTTable table = f.GetFont(iFont).GetTable(de.tag); hash.TransformBlock (table.m_bufTable.GetBuffer(), 0, (int)table.GetBuffer().GetPaddedLength(), table.m_bufTable.GetBuffer(), 0 ); PrevPos = de.offset; } } } byte[] usFlag = {0, 1}; hash.TransformFinalBlock(usFlag, 0,2); return hash.Hash; }
public DSIGInfo( string filename ) { fontfile = new OTFile(); tDSIG = null; Warn_TTCv1 = false; Warn_DSIG_in_memFonts = false; Warn_MalformedSIG = false; usNumSigs = 0; if ( !fontfile.open(filename) ) { throw new IOException("Cannot open file " + filename ); } TTCHeader ttc = null; if ( fontfile.IsCollection() ) { ttc = fontfile.GetTTCHeader(); if ( fontfile.GetTableManager().GetUnaliasedTableName(ttc.DsigTag) == "DSIG" ) { MBOBuffer buf = fontfile.ReadPaddedBuffer(ttc.DsigOffset, ttc.DsigLength); tDSIG = (Table_DSIG) fontfile.GetTableManager().CreateTableObject(ttc.DsigTag, buf); } for (uint i = 0; i < fontfile.GetNumFonts() ; i++) { OTFont fn = fontfile.GetFont(i); Table_DSIG memDSIG = (Table_DSIG) fn.GetTable("DSIG"); if (memDSIG != null) { Warn_DSIG_in_memFonts = true; break; } } } else { OTFont fn = fontfile.GetFont(0); tDSIG = (Table_DSIG) fn.GetTable("DSIG"); } HaveDSIG = ( ( tDSIG == null ) ? false : true ); // Officially we should only warn if HaveDSIG true if ( fontfile.IsCollection() && ttc.version != 0x00020000 ) Warn_TTCv1 = true; if ( HaveDSIG ) FurtherWork(); }
static int Main( string[] args ) { verbose = 0; if (args.Length == 0) { Console.WriteLine("CFFInfo [-v] [-v] [-v] fontfile"); return 0; } OTFile f = new OTFile(); Table_CFF tCFF = null; string filename = null; verbose = 0; for ( int i = 0; i < args.Length; i++ ) { if ( "-v" == args[i] ) verbose++; else filename = args[i]; } if ( !f.open(filename) ) { Console.WriteLine("Error: Cannot open {0} as font file", filename); return 0; } if ( f.GetNumFonts() != 1 ) Console.WriteLine("{0} contains {1} member fonts", filename, f.GetNumFonts() ); for (uint iFont = 0; iFont < f.GetNumFonts() ; iFont++) { OTFont fn = f.GetFont(iFont); tCFF = (Table_CFF) fn.GetTable("CFF "); Console.WriteLine("{0} CFF table: {1}", filename, ( tCFF == null ) ? "Absent" : "Present" ); if (tCFF == null) continue; /* header */ /* INDEX's */ Console.WriteLine("Name:\tcount={0}\tsize={1}", tCFF.Name.count, tCFF.Name.size); Console.WriteLine("TopDICT:\tcount={0}\tsize={1}", tCFF.TopDICT.count, tCFF.TopDICT.size); Console.WriteLine("String:\tcount={0}\tsize={1}", tCFF.String.count, tCFF.String.size); Console.WriteLine("GlobalSubr:\tcount={0}\tsize={1}", tCFF.GlobalSubr.count, tCFF.GlobalSubr.size); /* INDEX success */ var overlap = new DataOverlapDetector(); overlap.CheckForNoOverlap(0, tCFF.hdrSize); overlap.CheckForNoOverlap(tCFF.Name.begin, tCFF.Name.size); overlap.CheckForNoOverlap(tCFF.TopDICT.begin, tCFF.TopDICT.size); overlap.CheckForNoOverlap(tCFF.String.begin, tCFF.String.size); overlap.CheckForNoOverlap(tCFF.GlobalSubr.begin, tCFF.GlobalSubr.size); if ( verbose > 1 ) { Console.WriteLine("Region-hdr :\t{0}\t{1}", 0, tCFF.hdrSize); Console.WriteLine("Region-Name :\t{0}\t{1}", tCFF.Name.begin, tCFF.Name.size); Console.WriteLine("Region-TopDICT :\t{0}\t{1}", tCFF.TopDICT.begin, tCFF.TopDICT.size); Console.WriteLine("Region-String :\t{0}\t{1}", tCFF.String.begin, tCFF.String.size); Console.WriteLine("Region-GlobalSubr :\t{0}\t{1}", tCFF.GlobalSubr.begin, tCFF.GlobalSubr.size); } for(uint i = 0; i< tCFF.Name.count; i++) Console.WriteLine("Name: " + tCFF.Name.GetString(i)); try{ for(uint i = 0; i< tCFF.String.count; i++) { var a = tCFF.String.GetString(i); if ( verbose > 0 ) Console.WriteLine("String #{0}: {1}", i, a); } } catch (DecoderFallbackException) { for(uint i = 0; i< tCFF.String.count; i++) { var a = tCFF.String.GetUTFString(i); if ( verbose > 0 ) Console.WriteLine("String #{0}: {1}", i, a); } } for(uint iDICT = 0; iDICT < tCFF.TopDICT.count; iDICT++) { var curTopDICT = tCFF.GetTopDICT(iDICT); Console.WriteLine("FullName in TopDICT: " + curTopDICT.FullName); overlap.CheckForNoOverlap((uint)curTopDICT.offsetPrivate, (uint)curTopDICT.sizePrivate); if ( verbose > 1 ) Console.WriteLine("Region-Private :\t{0}\t{1}", curTopDICT.offsetPrivate, curTopDICT.sizePrivate); Console.WriteLine("Offset to Charset:\t" + curTopDICT.offsetCharset); Console.WriteLine("Offset to Encoding:\t" + curTopDICT.offsetEncoding); Table_CFF.DICTData topPrivateDict = null; try { topPrivateDict = tCFF.GetPrivate(curTopDICT); } catch (ArgumentOutOfRangeException e) { Console.WriteLine("Broken Private Dict:" + e.Message); } if (topPrivateDict != null && topPrivateDict.Subrs != 0) { var topPrivSubrs = tCFF.GetINDEX(curTopDICT.offsetPrivate + topPrivateDict.Subrs); overlap.CheckForNoOverlap(topPrivSubrs.begin, topPrivSubrs.size); if ( verbose > 1 ) Console.WriteLine("Region-PrivSubrs :\t{0}\t{1}", topPrivSubrs.begin, topPrivSubrs.size); } var CharStrings = tCFF.GetINDEX(curTopDICT.offsetCharStrings); Console.WriteLine("CharStrings count: " + CharStrings.count); overlap.CheckForNoOverlap(CharStrings.begin, CharStrings.size); if ( verbose > 1 ) Console.WriteLine("Region-CharStrings:\t{0}\t{1}", CharStrings.begin, CharStrings.size); if (curTopDICT.ROS != null) { Console.WriteLine("CID ROS: " + curTopDICT.ROS); Console.WriteLine("Offset to CID FDSelect:\t" + curTopDICT.offsetFDSelect); } if (curTopDICT.offsetFDArray > 0) { var FDArray = tCFF.GetINDEX(curTopDICT.offsetFDArray); overlap.CheckForNoOverlap(FDArray.begin, FDArray.size); if ( verbose > 1 ) Console.WriteLine("Region-FDArray :\t{0}\t{1}", FDArray.begin, FDArray.size); for(uint i = 0; i< FDArray.count; i++) { var FDict = tCFF.GetDICT(FDArray.GetData(i)); Console.WriteLine("CID FontDict #{0}: {1}", i, FDict.FontName); overlap.CheckForNoOverlap((uint)FDict.offsetPrivate, (uint)FDict.sizePrivate); if ( verbose > 1 ) Console.WriteLine("Region-CID FontDict #{2} :\t{0}\t{1}", FDict.offsetPrivate, FDict.sizePrivate, i); Table_CFF.DICTData FDictPrivate = null; try { FDictPrivate = tCFF.GetPrivate(FDict); } catch (ArgumentOutOfRangeException e) { Console.WriteLine("Broken CID FD Private Dict #{0}:{1}", i, e.Message); } if (FDictPrivate != null && FDictPrivate.Subrs != 0) { var PrivSubrs = tCFF.GetINDEX(FDict.offsetPrivate + FDictPrivate.Subrs); overlap.CheckForNoOverlap(PrivSubrs.begin, PrivSubrs.size); if ( verbose > 1 ) Console.WriteLine("Region-CID PrivSubrs #{2} :\t{0}\t{1}", PrivSubrs.begin, PrivSubrs.size, i); } } } } Console.WriteLine("Tested region: {0} of {1}", overlap.Occupied, tCFF.GetLength()); if ( overlap.ends != tCFF.GetLength() ) { } } return 0; }
static int Main( string[] args ) { if (args.Length == 0) { Console.WriteLine("DSIGInfo [-v] [-v] [-v] fontfile"); return 0; } OTFile f = new OTFile(); Table_DSIG tDSIG = null; int verbose = 0; string filename = null; for ( int i = 0; i < args.Length; i++ ) { if ( "-v" == args[i] ) verbose++; else filename = args[i]; } if ( !f.open(filename) ) { Console.WriteLine("Error: Cannot open {0} as font file", filename); return 0; } TTCHeader ttc = null; if ( f.IsCollection() ) { ttc = f.GetTTCHeader(); if ( f.GetTableManager().GetUnaliasedTableName(ttc.DsigTag) == "DSIG" ) { MBOBuffer buf = f.ReadPaddedBuffer(ttc.DsigOffset, ttc.DsigLength); tDSIG = (Table_DSIG) f.GetTableManager().CreateTableObject(ttc.DsigTag, buf); } } else { OTFont fn = f.GetFont(0); tDSIG = (Table_DSIG) fn.GetTable("DSIG"); } Console.WriteLine("{0} DSIG table: {1}", filename, ( tDSIG == null ) ? "Absent" : "Present" ); if (tDSIG == null) return 0; if ( f.IsCollection() && ttc.version != 0x00020000 ) Console.WriteLine("Warning: TTC has DSIG but header version is 0x{0}, != 0x00020000", ttc.version.ToString("X8")); if ( tDSIG.usNumSigs != 1 ) Console.WriteLine("NumSigs = {0}", tDSIG.usNumSigs); for ( uint v = 0; v < tDSIG.usNumSigs ; v++ ) { Table_DSIG.SignatureBlock sgb; try { sgb = tDSIG.GetSignatureBlock(v); } catch (IndexOutOfRangeException e) { Console.WriteLine("Error: Out of Range SignatureBlock {0}", v); break; } SignedCms cms = new SignedCms(); cms.Decode(sgb.bSignature); if ( cms.SignerInfos.Count > 1 ) Console.WriteLine( "#SignerInfos: {0}", cms.SignerInfos.Count ); foreach ( var si in cms.SignerInfos ) { Console.WriteLine(si.Certificate); } Console.WriteLine( "#Certificates: {0}", cms.Certificates.Count ); foreach ( var x509 in cms.Certificates ) { if ( verbose > 0 ) { Console.WriteLine(x509); } else { Console.WriteLine(x509.IssuerName.Name); } }; ASN1 spc = new ASN1(cms.ContentInfo.Content); ASN1 playload_oid = null; ASN1 oid = null; ASN1 digest = null; ASN1 obsolete = null; if ( Type.GetType("Mono.Runtime") == null ) { // DotNet is much saner! playload_oid = spc[0][0]; obsolete = spc[0][1][0]; oid = spc[1][0][0]; digest = spc[1][1]; } else { playload_oid = spc[0]; obsolete = spc[1][0]; oid = spc[2][0][0]; digest = spc[2][1]; } string algo = ASN1Convert.ToOid (oid); Console.WriteLine("Digest Algorithm: {0}", (new Oid(algo)).FriendlyName); byte[] Value = digest.Value; StringBuilder hexLine = new StringBuilder (); for (int i = 0; i < Value.Length; i++) { hexLine.AppendFormat ("{0} ", Value [i].ToString ("X2")); } hexLine.AppendFormat (Environment.NewLine); Console.WriteLine("{0} Digest: {1}", (new Oid(algo)).FriendlyName, hexLine); } return 0; }
static int Main( string[] args ) { verbose = 0; if (args.Length == 0) { Console.WriteLine("SVGInfo [-v] [-v] [-v] fontfile"); return 0; } OTFile f = new OTFile(); Table_SVG tSVG = null; string filename = null; verbose = 0; for ( int i = 0; i < args.Length; i++ ) { if ( "-v" == args[i] ) verbose++; else filename = args[i]; } if ( !f.open(filename) ) { Console.WriteLine("Error: Cannot open {0} as font file", filename); return 0; } for (uint i = 0; i < f.GetNumFonts() ; i++) { OTFont fn = f.GetFont(i); tSVG = (Table_SVG) fn.GetTable("SVG "); Console.WriteLine("{0} SVG table: {1}", filename, ( tSVG == null ) ? "Absent" : "Present" ); if (tSVG == null) continue; Console.WriteLine("version={0}, reserved={1}, offsetToSVGDocIndex={2}, numEntries={3}", tSVG.version, tSVG.reserved, tSVG.offsetToSVGDocIndex, tSVG.numEntries); Console.WriteLine("start\tend\tOffset\tLength"); Console.WriteLine("=====\t===\t======\t======"); for (uint j = 0; j < tSVG.numEntries ; j++) { var index = tSVG.GetDocIndexEntry(j); Console.WriteLine("{0}\t{1}\t{2}\t{3}", index.startGlyphID, index.endGlyphID, index.svgDocOffset, index.svgDocLength); } if ( verbose > 0 ) { XmlDocument doc = new XmlDocument(); if ( verbose < 2 ) doc.XmlResolver = null; //suppress network fetch for (uint j = 0; j < tSVG.numEntries ; j++) { var svgdoc = tSVG.GetDoc(j); Console.WriteLine("=={0}==", j); using(MemoryStream ms = new MemoryStream(svgdoc)) { doc.Load(ms); } using(XmlTextWriter writer = new XmlTextWriter(Console.Out)) { writer.Formatting = Formatting.Indented; writer.Indentation = 4; doc.Save(writer); writer.Flush(); } Console.WriteLine(""); //Newline } } } return 0; }