public WoffReader GetReader(uint offset) { var reader = new WoffReader(_buffer); reader.Offset = offset; return(reader); }
public static bool Decode(WoffReader flags, WoffReader glyphs, uint nPoints, IList <WoffPoint> points) { int y = 0; int x = 0; for (uint i = 0; i < nPoints; i++) { int dx = 0, dy = 0; byte flag = flags.ReadByte(); bool onCurve = (flag >> 7) == 0; flag &= 0x7f; if (flag < 10) { dx = 0; dy = WithSign(flag, ((flag & 14) << 7) + glyphs.ReadByte()); } else if (flag < 20) { dx = WithSign(flag, (((flag - 10) & 14) << 7) + glyphs.ReadByte()); dy = 0; } else if (flag < 84) { int b0 = flag - 20; int b1 = glyphs.ReadByte(); dx = WithSign(flag, 1 + (b0 & 0x30) + (b1 >> 4)); dy = WithSign(flag >> 1, 1 + ((b0 & 0x0c) << 2) + (b1 & 0x0f)); } else if (flag < 120) { int b0 = flag - 84; dx = WithSign(flag, 1 + ((b0 / 12) << 8) + glyphs.ReadByte()); dy = WithSign(flag >> 1, 1 + (((b0 % 12) >> 2) << 8) + glyphs.ReadByte()); } else if (flag < 124) { int b1 = glyphs.ReadByte(); int b2 = glyphs.ReadByte(); dx = WithSign(flag, (b1 << 4) + (b2 >> 4)); dy = WithSign(flag >> 1, ((b2 & 0x0f) << 8) + glyphs.ReadByte()); } else { dx = WithSign(flag, glyphs.ReadUInt16()); dy = WithSign(flag >> 1, glyphs.ReadUInt16()); } x += dx; y += dy; points.Add(new WoffPoint(x, y, onCurve)); } return(true); }
public bool SetComponents(WoffReader reader) { uint startOffset = reader.Offset; bool weHaveInstructions = false; ushort flags = MORE_COMPONENTS; while ((flags & MORE_COMPONENTS) > 0) { flags = reader.ReadUInt16(); weHaveInstructions |= (flags & WE_HAVE_INSTRUCTIONS) != 0; ushort argSize = WoffBuffer.SizeOfUShort; // 2-bytes glyph index if ((flags & ARG_1_AND_2_ARE_WORDS) > 0) { argSize += WoffBuffer.SizeOfUShort * 2; // 4-bytes } else { argSize += WoffBuffer.SizeOfUShort; // 2-bytes } if ((flags & WE_HAVE_A_SCALE) > 0) { argSize += WoffBuffer.SizeOfUShort; // 2-bytes } else if ((flags & WE_HAVE_AN_X_AND_Y_SCALE) > 0) { argSize += WoffBuffer.SizeOfUShort * 2; // 4-bytes } else if ((flags & WE_HAVE_A_TWO_BY_TWO) > 0) { argSize += WoffBuffer.SizeOfUShort * 4; // 8-bytes } if (!reader.Skip(argSize)) { return(false); } } uint endOffset = reader.Offset; _hasInstructions = weHaveInstructions; _componentLength = (ushort)(endOffset - startOffset); _components = reader.ReadByte(_componentLength, startOffset); Debug.Assert(reader.Offset == endOffset); return(reader.Offset == endOffset); }
private bool TransformGlyf() { var woffDirs = _woffFont.Directories; // By the specs: both glyf and loca tables must either be present in their transformed format // or with null transform applied to both tables. if (_glyfIndex == -1 || _locaIndex == -1) // Already verified! { Trace.TraceError("Glyph Compression Error: The glyf and loca tables must " + "either be present in their transformed format or with null transform applied to both tables."); return(false); } var locaDir = woffDirs[_locaIndex]; // By the specs: The transformLength of the transformed loca table must always be zero. Debug.Assert(locaDir.TransformLength == 0); if (locaDir.TransformLength != 0) { Trace.TraceError("Glyph Compression Error: " + "The transformLength of the transformed loca table must always be zero."); return(false); } var reader = new WoffReader(_woffDir.CompTable); var version = reader.ReadUInt32(); // Fixed version = 0x00000000 var numGlyphs = reader.ReadUInt16(); // Number of glyphs var indexFormat = reader.ReadUInt16(); // Offset format for loca table, should be consistent with indexToLocFormat of the original head table (see [OFF] specification) var nContourStreamSize = reader.ReadUInt32(); // Size of nContour stream in bytes var nPointsStreamSize = reader.ReadUInt32(); // Size of nPoints stream in bytes var flagStreamSize = reader.ReadUInt32(); // Size of flag stream in bytes var glyphStreamSize = reader.ReadUInt32(); // Size of glyph stream in bytes (a stream of variable-length encoded values, see description below) var compositeStreamSize = reader.ReadUInt32(); // Size of composite stream in bytes (a stream of variable-length encoded values, see description below) var bboxStreamSize = reader.ReadUInt32(); // Size of bbox data in bytes representing combined length of bboxBitmap (a packed bit array) and bboxStream (a stream of Int16 values) var instructionStreamSize = reader.ReadUInt32(); // Size of instruction stream (a stream of UInt8 values) Debug.Assert(version == 0x00000000); // https://dev.w3.org/webfonts/WOFF2/spec/#conform-mustRejectLoca // dst_length here is origLength in the spec uint expectedLocaDstLength = (uint)((indexFormat != 0 ? 4 : 2) * (numGlyphs + 1)); Debug.Assert(locaDir.OrigLength == expectedLocaDstLength); uint nContourStreamOffset = reader.Offset; var nPointsStreamOffset = nContourStreamOffset + nContourStreamSize; var flagStreamOffset = nPointsStreamOffset + nPointsStreamSize; var glyphStreamOffset = flagStreamOffset + flagStreamSize; var compositeStreamOffset = glyphStreamOffset + glyphStreamSize; var bboxStreamOffset = compositeStreamOffset + compositeStreamSize; var instructionStreamOffset = bboxStreamOffset + bboxStreamSize; var bboxBitmapOffset = bboxStreamOffset; // Safe because numGlyphs is bounded uint bboxBitmapSize = (uint)(((numGlyphs + 31) >> 5) << 2); // Store the glyph data type format _indexFormat = indexFormat; // Create the glyph array to store the transformed glyphs _glyphs = new WoffGlyph[numGlyphs]; _maxNumPoints = 0; // Stream of Int16 values representing number of contours for each glyph record // byte[] nContourStream = reader.ReadByte(nContourStreamSize, nContourStreamOffset); var nContourStream = new List <short>(); var compositeGlyphs = new List <ushort>(); // Store the composite glyph positions var nContoursCount = 0; // The combined number of contours for (ushort glyphIndex = 0; glyphIndex < numGlyphs; glyphIndex++) { var nContours = reader.ReadInt16(); nContourStream.Add(nContours); if (nContours == 0) { // Empty glyph _glyphs[glyphIndex] = new WoffGlyph(glyphIndex, 0, WoffGlyphType.None); } else if (nContours > 0) // -1 for signed, 0xffff for unsigned { // Simple glyph var glyph = new WoffGlyph(glyphIndex, nContours, WoffGlyphType.Simple); _glyphs[glyphIndex] = glyph; nContoursCount += nContours; } else if (nContours < 0) { // Composite glyph var glyph = new WoffGlyph(glyphIndex, nContours, WoffGlyphType.Composite); _glyphs[glyphIndex] = glyph; compositeGlyphs.Add(glyphIndex); } } Debug.Assert(nPointsStreamSize == nContoursCount); Debug.Assert(reader.Offset == nPointsStreamOffset); if (reader.Offset != nPointsStreamOffset) { Trace.TraceError("Glyph Compression Error: Invalid parameter {0}.", nContourStreamSize); return(false); } // Stream of values representing number of outline points for each contour in glyph records //byte[] nPointsStream = reader.ReadByte(nPointsStreamSize, nPointsStreamOffset); var nPointsStream = new List <ushort>(nContoursCount); for (int i = 0; i < nContoursCount; i++) { nPointsStream.Add(reader.Read255UInt16()); } Debug.Assert(reader.Offset == flagStreamOffset); if (reader.Offset != flagStreamOffset) { Trace.TraceError("Glyph Compression Error: Invalid parameter {0}.", nPointsStreamSize); return(false); } // Stream of UInt8 values representing flag values for each outline point. //byte[] flagStream = reader.ReadByte(flagStreamSize, flagStreamOffset); var flagReader = reader.GetReader(flagStreamOffset); //Debug.Assert(reader.Offset == glyphStreamOffset + flagStreamSize); //if (reader.Offset != glyphStreamOffset) //{ // Trace.TraceError("Glyph Compression Error: Invalid parameter {0}.", flagStreamSize); // return false; //} // Stream of bytes representing component flag values and associated composite glyph data if (compositeGlyphs.Count != 0) // If we have composite glyphs, CFF sources do not contain composite glyph { byte[] compositeStream = reader.ReadByte(compositeStreamSize, compositeStreamOffset); var compositeReader = new WoffReader(compositeStream); for (ushort i = 0; i < compositeGlyphs.Count; i++) { var glyphIndex = compositeGlyphs[i]; var glyph = _glyphs[glyphIndex]; Debug.Assert(glyph != null && glyph.GlyphType == WoffGlyphType.Composite); if (glyph.SetComponents(compositeReader) == false) { Trace.TraceError("Glyph Compression Error: Invalid composite stream. GlyphIndex = {0}.", glyphIndex); return(false); } } } // Stream of bytes representing point coordinate values using variable length encoding format (defined in subclause 5.2) //byte[] glyphStream = reader.ReadByte(glyphStreamSize, glyphStreamOffset); reader.Offset = glyphStreamOffset; int nPointsIndex = 0; //int nFlagIndex = 0; for (ushort glyphIndex = 0; glyphIndex < numGlyphs; glyphIndex++) { var glyph = _glyphs[glyphIndex]; var nContours = nContourStream[glyphIndex]; if (nContours == 0) { // Empty glyph - do nothing Debug.Assert(glyph != null && glyph.GlyphType == WoffGlyphType.None); } else if (nContours > 0) { // Simple glyph Debug.Assert(glyph != null && glyph.GlyphType == WoffGlyphType.Simple); ushort nPoints = 0; var endPtsOfContours = new ushort[nContours]; for (ushort i = 0; i < nContours; i++) { nPoints += nPointsStream[nPointsIndex + i]; endPtsOfContours[i] = (ushort)(nPoints - 1); } nPointsIndex += nContours; var glyphPoints = glyph.Contours; Debug.Assert(glyphPoints != null); if (WoffTriplet.Decode(flagReader, reader, nPoints, glyphPoints) == false) { Trace.TraceError("Glyph Compression Error: Triplet decoding failed. GlyphIndex = " + glyphIndex); return(false); } glyph.InstructionLength = reader.Read255UInt16(); glyph.NumberOfPoints = nPoints; glyph.EndPtsOfContours = endPtsOfContours; _maxNumPoints = Math.Max(_maxNumPoints, nPoints); } else { // Composite glyph Debug.Assert(glyph != null && glyph.GlyphType == WoffGlyphType.Composite); if (glyph.HasInstructions) { glyph.InstructionLength = reader.Read255UInt16(); } } } // Store xMin of the bounding box to reconstruct 'hmtx' _glyphAdvances = new short[numGlyphs]; // Bitmap (a numGlyphs-long bit array) indicating explicit bounding boxes byte[] bboxBitmap = reader.ReadByte(bboxBitmapSize, bboxBitmapOffset); const int bitIndex = 7; for (ushort glyphIndex = 0; glyphIndex < numGlyphs; glyphIndex++) { var glyph = _glyphs[glyphIndex]; var glyphType = glyph.GlyphType; bool hasBbox = false; if ((bboxBitmap[glyphIndex >> 3] & (0x80 >> (glyphIndex & bitIndex))) > 0) { hasBbox = true; } if (glyphType == WoffGlyphType.None) { if (hasBbox) { // An empty glyph must not have a bbox. Trace.TraceError("Glyph Compression Error: An empty glyph must not have a bbox. GlyphIndex = {0}.", glyphIndex); return(false); } } else if (glyphType == WoffGlyphType.Composite) { if (!hasBbox) { // A composite glyphs must have an explicit bbox. Trace.TraceError("Glyph Compression Error: A composite glyph must have an explicit bbox. GlyphIndex = {0}.", glyphIndex); return(false); } } if (hasBbox) { glyph.XMin = reader.ReadInt16(); glyph.YMin = reader.ReadInt16(); glyph.XMax = reader.ReadInt16(); glyph.YMax = reader.ReadInt16(); } else { glyph.RecalcBounds(); } _glyphAdvances[glyphIndex] = glyph.XMin; } // Stream of Int16 values representing glyph bounding box data //byte[] bboxStream = reader.ReadByte(bboxStreamSize, bboxStreamOffset); Debug.Assert(reader.Offset == instructionStreamOffset); if (reader.Offset != instructionStreamOffset) { Trace.TraceError("Glyph Compression Error: Invalid parameter {0}.", bboxStreamSize); return(false); } // Stream of UInt8 values representing a set of instructions for each corresponding glyph //byte[] instructionStream = reader.ReadByte(instructionStreamSize, instructionStreamOffset); if (instructionStreamSize != 0) // Check if we have instructions, CFF sources do not have instructions. { for (ushort glyphIndex = 0; glyphIndex < numGlyphs; glyphIndex++) { var glyph = _glyphs[glyphIndex]; if (glyph.InstructionLength > 0) { glyph.Instructions = reader.ReadByte(glyph.InstructionLength); } } } Debug.Assert(reader.Offset == instructionStreamOffset + instructionStreamSize); if (reader.Offset != instructionStreamOffset + instructionStreamSize) { Trace.TraceError("Glyph Compression Error: Invalid parameter {0}.", instructionStreamSize); return(false); } // Serialize the transformed glyphs... if (SerializeGlyf(indexFormat) == false) { return(false); } return(true); }
private bool TransformHmtx() { var glyfTable = _woffFont.Tables[_glyfIndex] as WoffTableGlyf; Debug.Assert(glyfTable != null); if (glyfTable == null) { return(false); } if (_hheaIndex == -1 || _hmtxIndex == -1) { return(false); } var glyphs = glyfTable.Glyphs; var glyphAdvances = glyfTable.GlyphsAdvances; var woffDirs = _woffFont.Directories; var hheaDir = woffDirs[_hheaIndex]; // Get numberOfHMetrics, https://www.microsoft.com/typography/otspec/hhea.htm var hheaReader = new WoffReader(hheaDir.OrigTable); // Skip 34 to reach 'hhea' numberOfHMetrics if (hheaReader.Skip(34) == false) { return(false); } ushort numOfHMetrics = hheaReader.ReadUInt16(); var reader = new WoffReader(_woffDir.CompTable); var flags = reader.ReadByte(); // By the specs: When hmtx transform is indicated by the table directory, // the Flags (bits 0 or 1 or both) must be set. if ((flags & 0x01) != 1 && (flags & 0x02) != 1) { Trace.TraceError("Hmtx Compression Error: When hmtx transform is indicated by the table directory, " + "the Flags (bits 0 or 1 or both) must be set."); return(false); } // By the specs: Bits 2-7 are reserved and must be zero. if ((flags & 0xFC) != 0) { Trace.TraceError("Hmtx Compression Error: Bits 2-7 are reserved and must be zero."); } int numOfGlyphs = glyphs.Length; Debug.Assert(glyphAdvances.Length == numOfGlyphs); // The number of glyphs numGlyphs can be 0, if there is no 'glyf' but cannot then xform 'hmtx'. Debug.Assert(numOfHMetrics <= numOfGlyphs); if (numOfHMetrics > numOfGlyphs) { return(false); } // https://www.microsoft.com/typography/otspec/hmtx.htm // "...only one entry need be in the array, but that entry is required." if (numOfHMetrics < 1) { return(false); } bool hasProportionalLsbs = (flags & 0x01) == 0; bool hasMonospaceLsbs = (flags & 0x02) == 0; IList <ushort> advanceWidths = new List <ushort>(); IList <short> lsbs = new List <short>(); for (ushort glyphIndex = 0; glyphIndex < numOfHMetrics; glyphIndex++) { advanceWidths.Add(reader.ReadUInt16()); } for (ushort glyphIndex = 0; glyphIndex < numOfHMetrics; glyphIndex++) { if (hasProportionalLsbs) { lsbs.Add(reader.ReadInt16()); } else { lsbs.Add(glyphAdvances[glyphIndex]); } } for (ushort glyphIndex = numOfHMetrics; glyphIndex < numOfGlyphs; glyphIndex++) { if (hasMonospaceLsbs) { lsbs.Add(reader.ReadInt16()); } else { lsbs.Add(glyphAdvances[glyphIndex]); } } return(SerializeHmtx(advanceWidths, lsbs, numOfHMetrics, numOfGlyphs)); }