var r = require('../') var chai = require('chai') , expect = chai.expect // - - - - - - - - - - - - - - - - - - - - // Definition of STAT table structures var AxisRecord = new r.Struct({ AxisTag: new r.String(4), // e.g. 'ital', 'wght' AxisNameID: r.uint16, AxisOrdering: r.uint16, }) var AxisValue = new r.VersionedStruct( r.uint16, { header: { AxisIndex: r.uint16, Flags: r.uint16, ValueNameID: r.uint16, }, 1: { Value: r.fixed32be, // Fixed 16.16 }, 2: { NominalValue: r.fixed32be, // Fixed 16.16 RangeMinValue: r.fixed32be, // Fixed 16.16 RangeMaxValue: r.fixed32be, // Fixed 16.16 }, 3: { Value: r.fixed32be, // Fixed 16.16 LinkedValue: r.fixed32be, // Fixed 16.16 }, }) // Testing permutations of using second-level struct to handle // array of pointers var AxisValueArray if(0){ // Works, but array size hard-coded AxisValueArray = new r.Struct( { AxisValues: new r.Array( new r.Pointer(r.uint16, AxisValue), 6), }) }else if(0){ // Fails with "Error: Not a fixed size" AxisValueArray = new r.Struct( { AxisValues: new r.Array( new r.Pointer(r.uint16, AxisValue), 'AxisValueCount'), }) }else{ // Array size function sees null/empty parent argument?!? AxisValueArray = new r.Struct( { AxisValues: new r.Array( new r.Pointer(r.uint16, AxisValue), function(parent){ console.log("**D this: %j", this) console.log("**D parent: %j", parent) return 6 }), }) // **D this: {} // **D parent: {} } var structSTATTable = new r.VersionedStruct( r.uint32, { 0x00010001: { DesignAxisSize: r.uint16, DesignAxisCount: r.uint16, OffsetToDesignAxes: new r.Pointer(r.uint32, new r.Array( AxisRecord, 'DesignAxisCount')), AxisValueCount: r.uint16, // Used to display the offsets in the second-level array of pointers for debugging // OffsetToAxisValueOffsets: // new r.Pointer(r.uint32, // new r.Array( // r.uint16, // 'AxisValueCount')), // Fails with "Error: Unknown version 6" // OffsetToAxisValueOffsets: // new r.Pointer(r.uint32, // new r.Array( // new r.Pointer(r.uint16, AxisValue), // 6) // ), // Fails with "Error: Unknown version 6" // OffsetToAxisValueOffsets: // new r.Pointer(r.uint32, // new r.Array( // new r.Pointer(r.uint16, AxisValue), // 'AxisValueCount') // ), // Works when 6 hard-coded in separate struct of array of pointers // Fails when separate struct tries to access 'AxisValueCount' OffsetToAxisValueOffsets: new r.Pointer(r.uint32, AxisValueArray), ElidedFallbackNameID: r.uint16, }, }) // - - - - - - - - - - - - - - - - - - - - // An example Opentype font file STAT table // This data from the test font file found at // https://github.com/unicode-org/text-rendering-tests/blob/master/fonts/Selawik-variable.ttf // As dumped: // STAT 0xECB0D1A2 120 98924 001826C // TSI0 0xCC020E14 3112 99044 00182E4 // 0018260: 0001 0001 ......... ...... // 0018270: 0008 0002 0000 0014 0006 0000 0024 0103 .............$.. // 0018280: 6974 616c 0100 0001 7767 6874 0100 0000 ital....wght.... // 0018290: 000c 0018 0024 0030 003c 0048 0001 0000 .....$.0.<.H.... // 00182a0: 0002 0103 0000 0000 0001 0001 0000 0101 ................ // 00182b0: 012c 0000 0001 0001 0000 0102 015e 0000 .,...........^.. // 00182c0: 0001 0001 0000 0103 0190 0000 0001 0001 ................ // 00182d0: 0000 0104 0258 0000 0001 0001 0000 0105 .....X.......... // 00182e0: 02bc 0000 0000 0050 0000 0000 0001 00dc .......P........ // That dump exploded and annotated: var inputSTATTable = [ // 0000 0x00, 0x01, 0x00, 0x01, // 0000 Version 0x00, 0x08, // 0004 uint16 designAxisSize 0x00, 0x02, // 0006 uint16 designAxisCount 0x00, 0x00, 0x00, 0x14, // 0008 LOffset offsetToDesignAxes 0x00, 0x06, // 000C uint16 axisValueCount 0x00, 0x00, 0x00, 0x24, // 000E LOffset offsetToAxisValueOffsets 0x01, 0x03, // 0012 uint16/NameID elidedFallbackNameID // 0014 designAxes[2] 0x69, 0x74, 0x61, 0x6c, // 0000 [0] Tag axisTag 'ital' 0x01, 0x00, // 0004 [0] uint16 axisNameID 0x00, 0x01, // 0006 [0] uint16 axisOrdering 0x77, 0x67, 0x68, 0x74, // 0008 [1] Tag axisTag 'wght' 0x01, 0x00, // 000C [1] uint16 axisNameID 0x00, 0x00, // 000E [1] uint16 axisOrdering // 0024 Offset axisValueOffsets[6] 0x00, 0x0c, // 0000 axisValueOffsets[0] 0x00, 0x18, // 0002 axisValueOffsets[1] 0x00, 0x24, // 0004 axisValueOffsets[2] 0x00, 0x30, // 0006 axisValueOffsets[3] 0x00, 0x3c, // 0008 axisValueOffsets[4] 0x00, 0x48, // 000A axisValueOffsets[5] // 0030 000C axisValue[0] 0x00, 0x01, // 0000 uint16 format 0x00, 0x00, // 0002 uint16 axisIndex 0x00, 0x02, // 0004 uint16 flags 0x01, 0x03, // 0006 uint16 valueNameID 0x00, 0x00, 0x00, 0x00, // 0008 Fixed(16.16) value 0.0 // 003C 0018 axisValue[1] 0x00, 0x01, // 0000 uint16 format 0x00, 0x01, // 0002 uint16 axisIndex 0x00, 0x00, // 0004 uint16 flags 0x01, 0x01, // 0006 uint16 valueNameID 0x01, 0x2c, 0x00, 0x00, // 0008 Fixed(16.16) value 300.0 // 0048 0024 axisValue[2] 0x00, 0x01, // 0000 uint16 format 0x00, 0x01, // 0002 uint16 axisIndex 0x00, 0x00, // 0004 uint16 flags 0x01, 0x02, // 0006 uint16 valueNameID 0x01, 0x5e, 0x00, 0x00, // 0008 Fixed(16.16) value 350.0 // 0054 0030 axisValue[3] 0x00, 0x01, // 0000 uint16 format 0x00, 0x01, // 0002 uint16 axisIndex 0x00, 0x00, // 0004 uint16 flags 0x01, 0x03, // 0006 uint16 valueNameID 0x01, 0x90, 0x00, 0x00, // 0008 Fixed(16.16) value 400.0 // 0060 003C axisValue[4] 0x00, 0x01, // 0000 uint16 format 0x00, 0x01, // 0002 uint16 axisIndex 0x00, 0x00, // 0004 uint16 flags 0x01, 0x04, // 0006 uint16 valueNameID 0x02, 0x58, 0x00, 0x00, // 0008 Fixed(16.16) value 600.0 // 006C 0048 axisValue[5] 0x00, 0x01, // 0000 uint16 format 0x00, 0x01, // 0002 uint16 axisIndex 0x00, 0x00, // 0004 uint16 flags 0x01, 0x05, // 0006 uint16 valueNameID 0x02, 0xbc, 0x00, 0x00 // 0008 Fixed(16.16) value 700.0 ]; // 0078 // The object as expected from successful parsing of above data var expectedSTATTable = { "version": 0x00010001, // s.b. "Version" or "majorVersion"/"minorVersion" "DesignAxisSize": 8, "DesignAxisCount": 2, "OffsetToDesignAxes": [ { "AxisTag": 'ital', // 'ital' 1769234796 "AxisNameID": 256, "AxisOrdering": 1 }, { "AxisTag": 'wght', // 'wght' 2003265652 "AxisNameID": 256, "AxisOrdering": 0 } ], "AxisValueCount": 6, "OffsetToAxisValueOffsets": { "AxisValues": [ { "version": 1, // s.b. "Format" "AxisIndex": 0, "Flags": 2, "ValueNameID": 259, "Value": 0 // 0.0 0 }, { "version": 1, "AxisIndex": 1, "Flags": 0, "ValueNameID": 257, "Value": 300.0 // 300.0 19660800 }, { "version": 1, "AxisIndex": 1, "Flags": 0, "ValueNameID": 258, "Value": 350.0 // 350.0 22937600 }, { "version": 1, "AxisIndex": 1, "Flags": 0, "ValueNameID": 259, "Value": 400.0 // 400.0 26214400 }, { "version": 1, "AxisIndex": 1, "Flags": 0, "ValueNameID": 260, "Value": 600.0 // 600.0 39321600 }, { "version": 1, "AxisIndex": 1, "Flags": 0, "ValueNameID": 261, "Value": 700.0 // 700.0 45875200 } ] }, "ElidedFallbackNameID": 259 } // - - - - - - - - - - - - - - - - - - - - // Isolated axisValueOffsets and axisValues[] var inputSTATAxisValuesOffsetsArray = [ // 0000 0x00, 0x0c, // 0000 axisValueOffsets[0] 0x00, 0x18, // 0002 axisValueOffsets[1] 0x00, 0x24, // 0004 axisValueOffsets[2] 0x00, 0x30, // 0006 axisValueOffsets[3] 0x00, 0x3c, // 0008 axisValueOffsets[4] 0x00, 0x48, // 000A axisValueOffsets[5] // 000C axisValue[0] 0x00, 0x01, // 0000 uint16 format 0x00, 0x00, // 0002 uint16 axisIndex 0x00, 0x02, // 0004 uint16 flags 0x01, 0x03, // 0006 uint16 valueNameID 0x00, 0x00, 0x00, 0x00, // 0008 Fixed(16.16) value 0.0 // 0018 axisValue[1] 0x00, 0x01, // 0000 uint16 format 0x00, 0x01, // 0002 uint16 axisIndex 0x00, 0x00, // 0004 uint16 flags 0x01, 0x01, // 0006 uint16 valueNameID 0x01, 0x2c, 0x00, 0x00, // 0008 Fixed(16.16) value 300.0 // 0024 axisValue[2] 0x00, 0x01, // 0000 uint16 format 0x00, 0x01, // 0002 uint16 axisIndex 0x00, 0x00, // 0004 uint16 flags 0x01, 0x02, // 0006 uint16 valueNameID 0x01, 0x5e, 0x00, 0x00, // 0008 Fixed(16.16) value 350.0 // 0030 axisValue[3] 0x00, 0x01, // 0000 uint16 format 0x00, 0x01, // 0002 uint16 axisIndex 0x00, 0x00, // 0004 uint16 flags 0x01, 0x03, // 0006 uint16 valueNameID 0x01, 0x90, 0x00, 0x00, // 0008 Fixed(16.16) value 400.0 // 003C axisValue[4] 0x00, 0x01, // 0000 uint16 format 0x00, 0x01, // 0002 uint16 axisIndex 0x00, 0x00, // 0004 uint16 flags 0x01, 0x04, // 0006 uint16 valueNameID 0x02, 0x58, 0x00, 0x00, // 0008 Fixed(16.16) value 600.0 // 0048 axisValue[5] 0x00, 0x01, // 0000 uint16 format 0x00, 0x01, // 0002 uint16 axisIndex 0x00, 0x00, // 0004 uint16 flags 0x01, 0x05, // 0006 uint16 valueNameID 0x02, 0xbc, 0x00, 0x00 // 0008 Fixed(16.16) value 700.0 ]; // 0078 // The object as expected from successful parsing of above data var expectedSTATAxisValuesArray = { "AxisValues": [ { "version": 1, "AxisIndex": 0, "Flags": 2, "ValueNameID": 259, "Value": 0 // 0.0 0 }, { "version": 1, "AxisIndex": 1, "Flags": 0, "ValueNameID": 257, "Value": 300.0 // 300.0 19660800 }, { "version": 1, "AxisIndex": 1, "Flags": 0, "ValueNameID": 258, "Value": 350.0 // 350.0 22937600 }, { "version": 1, "AxisIndex": 1, "Flags": 0, "ValueNameID": 259, "Value": 400.0 // 400.0 26214400 }, { "version": 1, "AxisIndex": 1, "Flags": 0, "ValueNameID": 260, "Value": 600.0 // 600.0 39321600 }, { "version": 1, "AxisIndex": 1, "Flags": 0, "ValueNameID": 261, "Value": 700.0 // 700.0 45875200 } ] } // - - - - - - - - - - - - - - - - - - - - describe('Check VersionedStruct usage - text with STAT Table sample', function(){ // - - - - - - - - - - - - - - - - - - - - if(0){ // Test parsing internal data AxisValues array describe('STAT AxisValues offsets and array', function(){ var stream = new r.DecodeStream(new Buffer(inputSTATAxisValuesOffsetsArray)) var result = AxisValueArray.decode(stream) //console.log(' decoded AxisValues: %j', result) it('decode result matches expected data', function(){ expect(result).is.deep.equal(expectedSTATAxisValuesArray) }) }) } // Test parsing entire AxisValues array describe('STAT Table data', function(){ var stream = new r.DecodeStream(new Buffer(inputSTATTable)) var result = structSTATTable.decode(stream) //console.log(' decoded STAT table: %j', result) it('decode result matches expected data', function(){ expect(result).is.deep.equal(expectedSTATTable) }) }) }) /* */ // vim:ts=2:et:sw=2:sidescroll=2:ft=javascript