// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System; using System.Collections.Generic; namespace SixLabors.Fonts.Tables { /// /// Provides the WOFF2 triplet encoding lookup table. Each of the 128 index values /// maps to a that defines how a glyph coordinate /// triplet (flag, x, y) is packed into bytes. /// /// /// /// Source code is based on https://github.com/LayoutFarm/Typography. /// internal class TripleEncodingTable { /// /// Gets the singleton instance of the triplet encoding table. /// public static readonly TripleEncodingTable EncTable = new TripleEncodingTable(); private readonly List records = new List(); private TripleEncodingTable() => this.BuildTable(); /// /// Gets the at the specified index (0–127). /// /// The triplet encoding index derived from the glyph flag byte. /// The encoding record for the given index. public TripleEncodingRecord this[int i] => this.records[i]; private void BuildTable() { // Each of the 128 index values define the following properties and specified in details in the table below: // Byte count(total number of bytes used for this set of coordinate values including one byte for 'flag' value). // Number of bits used to represent X coordinate value(X bits). // Number of bits used to represent Y coordinate value(Y bits). // An additional incremental amount to be added to X bits value(delta X). // An additional incremental amount to be added to Y bits value(delta Y). // The sign of X coordinate value(X sign). // The sign of Y coordinate value(Y sign). // Please note that "Byte Count" field reflects total size of the triplet(flag, xCoordinate, yCoordinate), // including ‘flag’ value that is encoded in a separate stream. // Triplet Encoding // Index ByteCount Xbits Ybits DeltaX DeltaY Xsign Ysign // (set 1.1) // 0 2 0 8 N/A 0 N/A - // 1 0 + // 2 256 - // 3 256 + // 4 512 - // 5 512 + // 6 768 - // 7 768 + // 8 1024 - // 9 1024 + this.BuildRecords(2, 0, 8, Array.Empty(), new ushort[] { 0, 256, 512, 768, 1024 }); // Index ByteCount Xbits Ybits DeltaX DeltaY Xsign Ysign // (set 1.2) // 10 2 8 0 0 N/A - N/A // 11 0 + // 12 256 - // 13 256 + // 14 512 - // 15 512 + // 16 768 - // 17 768 + // 18 1024 - // 19 1024 + this.BuildRecords(2, 8, 0, new ushort[] { 0, 256, 512, 768, 1024 }, Array.Empty()); // Index ByteCount Xbits Ybits DeltaX DeltaY Xsign Ysign // (set 2.1) // 20 2 4 4 1 1 - - // 21 1 + - // 22 1 - + // 23 1 + + // 24 17 - - // 25 17 + - // 26 17 - + // 27 17 + + // 28 33 - - // 29 33 + - // 30 33 - + // 31 33 + + // 32 49 - - // 33 49 + - // 34 49 - + // 35 49 + + this.BuildRecords(2, 4, 4, new ushort[] { 1 }, new ushort[] { 1, 17, 33, 49 }); // Index ByteCount Xbits Ybits DeltaX DeltaY Xsign Ysign // (set 2.2) // 36 2 4 4 17 1 - - // 37 1 + - // 38 1 - + // 39 1 + + // 40 17 - - // 41 17 + - // 42 17 - + // 43 17 + + // 44 33 - - // 45 33 + - // 46 33 - + // 47 33 + + // 48 49 - - // 49 49 + - // 50 49 - + // 51 49 + + this.BuildRecords(2, 4, 4, new ushort[] { 17 }, new ushort[] { 1, 17, 33, 49 }); // Index ByteCount Xbits Ybits DeltaX DeltaY Xsign Ysign // (set 2.3) // 52 2 4 4 33 1 - - // 53 1 + - // 54 1 - + // 55 1 + + // 56 17 - - // 57 17 + - // 58 17 - + // 59 17 + + // 60 33 - - // 61 33 + - // 62 33 - + // 63 33 + + // 64 49 - - // 65 49 + - // 66 49 - + // 67 49 + + this.BuildRecords(2, 4, 4, new ushort[] { 33 }, new ushort[] { 1, 17, 33, 49 }); // Index ByteCount Xbits Ybits DeltaX DeltaY Xsign Ysign // (set 2.4) // 68 2 4 4 49 1 - - // 69 1 + - // 70 1 - + // 71 1 + + // 72 17 - - // 73 17 + - // 74 17 - + // 75 17 + + // 76 33 - - // 77 33 + - // 78 33 - + // 79 33 + + // 80 49 - - // 81 49 + - // 82 49 - + // 83 49 + + this.BuildRecords(2, 4, 4, new ushort[] { 49 }, new ushort[] { 1, 17, 33, 49 }); // Index ByteCount Xbits Ybits DeltaX DeltaY Xsign Ysign // (set 3.1) // 84 3 8 8 1 1 - - // 85 1 + - // 86 1 - + // 87 1 + + // 88 257 - - // 89 257 + - // 90 257 - + // 91 257 + + // 92 513 - - // 93 513 + - // 94 513 - + // 95 513 + + this.BuildRecords(3, 8, 8, new ushort[] { 1 }, new ushort[] { 1, 257, 513 }); // Index ByteCount Xbits Ybits DeltaX DeltaY Xsign Ysign // (set 3.2) // 96 3 8 8 257 1 - - // 97 1 + - // 98 1 - + // 99 1 + + // 100 257 - - // 101 257 + - // 102 257 - + // 103 257 + + // 104 513 - - // 105 513 + - // 106 513 - + // 107 513 + + this.BuildRecords(3, 8, 8, new ushort[] { 257 }, new ushort[] { 1, 257, 513 }); // Index ByteCount Xbits Ybits DeltaX DeltaY Xsign Ysign // (set 3.3) // 108 3 8 8 513 1 - - // 109 1 + - // 110 1 - + // 111 1 + + // 112 257 - - // 113 257 + - // 114 257 - + // 115 257 + + // 116 513 - - // 117 513 + - // 118 513 - + // 119 513 + + this.BuildRecords(3, 8, 8, new ushort[] { 513 }, new ushort[] { 1, 257, 513 }); // Index ByteCount Xbits Ybits DeltaX DeltaY Xsign Ysign // (set 4) // 120 4 12 12 0 0 - - // 121 + - // 122 - + // 123 + + this.BuildRecords(4, 12, 12, new ushort[] { 0 }, new ushort[] { 0 }); // Index ByteCount Xbits Ybits DeltaX DeltaY Xsign Ysign // (set 5) // 124 5 16 16 0 0 - - // 125 + - // 126 - + // 127 + + this.BuildRecords(5, 16, 16, new ushort[] { 0 }, new ushort[] { 0 }); } private void BuildRecords(byte byteCount, byte xbits, byte ybits, ushort[] deltaXs, ushort[] deltaYs) { if (deltaXs.Equals(Array.Empty())) { // (set 1.1) for (int y = 0; y < deltaYs.Length; ++y) { this.AddRecord(byteCount, xbits, ybits, 0, deltaYs[y], 0, -1); this.AddRecord(byteCount, xbits, ybits, 0, deltaYs[y], 0, 1); } } else if (deltaYs.Equals(Array.Empty())) { // (set 1.2) for (int x = 0; x < deltaXs.Length; ++x) { this.AddRecord(byteCount, xbits, ybits, deltaXs[x], 0, -1, 0); this.AddRecord(byteCount, xbits, ybits, deltaXs[x], 0, 1, 0); } } else { // set 2.1, - set5 for (int x = 0; x < deltaXs.Length; ++x) { ushort deltaX = deltaXs[x]; for (int y = 0; y < deltaYs.Length; ++y) { ushort deltaY = deltaYs[y]; this.AddRecord(byteCount, xbits, ybits, deltaX, deltaY, -1, -1); this.AddRecord(byteCount, xbits, ybits, deltaX, deltaY, 1, -1); this.AddRecord(byteCount, xbits, ybits, deltaX, deltaY, -1, 1); this.AddRecord(byteCount, xbits, ybits, deltaX, deltaY, 1, 1); } } } } private void AddRecord(byte byteCount, byte xbits, byte ybits, ushort deltaX, ushort deltaY, sbyte xsign, sbyte ysign) { var rec = new TripleEncodingRecord(byteCount, xbits, ybits, deltaX, deltaY, xsign, ysign); this.records.Add(rec); } } }