public static int ParseOperand(FontParser parser, int b0) { if (b0 == 28) { return(parser.ReadInt16()); } if (b0 == 29) { return(parser.ReadInt32()); } if (b0 == 30) { // A floating point value which we really don't need - skipping! while (true) { byte b = parser.ReadByte(); int n1 = b >> 4; int n2 = b & 15; if (n1 == 15 || n2 == 15) { break; } } return(0); } if (b0 >= 32 && b0 <= 246) { return(b0 - 139); } if (b0 >= 247 && b0 <= 250) { return((b0 - 247) * 256 + parser.ReadByte() + 108); } if (b0 >= 251 && b0 <= 254) { return(-(b0 - 251) * 256 - parser.ReadByte() - 108); } return(0); }
public static int LoadGlyphCoordinate(FontParser parser, byte flag, int previousValue, int shortVectorBitMask, int sameBitMask) { int v; if ((flag & shortVectorBitMask) > 0) { // The coordinate is 1 byte long. v = parser.ReadByte(); // The "same" bit is re-used for short values to signify the sign of the value. if ((flag & sameBitMask) == 0) { v = -v; } v = previousValue + v; } else { // The coordinate is 2 bytes long. // If the same bit is set, the coordinate is the same as the previous coordinate. if ((flag & sameBitMask) > 0) { v = previousValue; } else { // Parse the coordinate as a signed 16-bit delta value. v = previousValue + parser.ReadInt16(); } } return(v); }
public static Dictionary <int, List <int> > ParseCFFDict(FontParser parser, int length) { // Create the results set: Dictionary <int, List <int> > results = new Dictionary <int, List <int> >(); // Our first values set: List <int> values = new List <int>(); // Note that rather awkwardly the key comes *after* the set of values. int max = parser.Position + length; // While there's more data.. while (parser.Position < max) { // Read the state byte: int state = parser.ReadByte(); if (state < 22) { if (state == 12) { // 2 byte key code. state = 1200 + parser.ReadByte(); } // Push values: results[state] = values; // Clear: values = new List <int>(); } else { // Read the operand: int operand = ParseOperand(parser, state); // Push: values.Add(operand); } } return(results); }
private static Glyph[] LoadIndex(FontParser parser,CffGlyphParser cffParser){ // Read the index which contains a bunch of char strings. // Each charstring is a postscript glyph definition. // How many are in here? int count=parser.ReadUInt16(); if(count==0){ return null; } // Create the offset set: int[] offsets=new int[count+1]; // Read the offset size: int offsetSize=parser.ReadByte(); // Read each offset: for(int i=0;i<=count;i++){ // Read the current offset: offsets[i]=parser.ReadOffset(offsetSize); } // Grab the object offset, minus one as their not zero based: int objectOffset=parser.Position-1; // Create the glyph set: Glyph[] glyphs=new Glyph[offsets.Length-1]; // For each one.. for(int i=0;i<glyphs.Length;i++){ // Get the (relative) indices: int startIndex=offsets[i]; int length=offsets[i+1]-startIndex; // Load the glyph now, which starts at startIndex+objectOffset: Glyph glyph=cffParser.LoadGlyph(startIndex+objectOffset,length); // Add to the set: glyphs[i]=glyph; } // Seek over the table: parser.Position=objectOffset+offsets[count]; return glyphs; }
private static Glyph[] LoadIndex(FontParser parser, CffGlyphParser cffParser) { // Read the index which contains a bunch of char strings. // Each charstring is a postscript glyph definition. // How many are in here? int count = parser.ReadUInt16(); if (count == 0) { return(null); } // Create the offset set: int[] offsets = new int[count + 1]; // Read the offset size: int offsetSize = parser.ReadByte(); // Read each offset: for (int i = 0; i <= count; i++) { // Read the current offset: offsets[i] = parser.ReadOffset(offsetSize); } // Grab the object offset, minus one as their not zero based: int objectOffset = parser.Position - 1; // Create the glyph set: Glyph[] glyphs = new Glyph[offsets.Length - 1]; // For each one.. for (int i = 0; i < glyphs.Length; i++) { // Get the (relative) indices: int startIndex = offsets[i]; int length = offsets[i + 1] - startIndex; // Load the glyph now, which starts at startIndex+objectOffset: Glyph glyph = cffParser.LoadGlyph(startIndex + objectOffset, length); // Add to the set: glyphs[i] = glyph; } // Seek over the table: parser.Position = objectOffset + offsets[count]; return(glyphs); }
private static CffSubPosition[] LoadSubIndex(FontParser parser) { // How many are in here? int count = parser.ReadUInt16(); if (count == 0) { return(null); } // Create the offset set: int[] offsets = new int[count + 1]; // Read the offset size: int offsetSize = parser.ReadByte(); // Read each offset: for (int i = 0; i <= count; i++) { // Read the current offset: offsets[i] = parser.ReadOffset(offsetSize); } // Minus one as their not zero based: int objectOffset = parser.Position - 1; // Seek over the table: parser.Position = objectOffset + offsets[count]; // Create the result set: CffSubPosition[] results = new CffSubPosition[offsets.Length - 1]; // For each one.. for (int i = 0; i < results.Length; i++) { // Get the (relative) indices: int startIndex = offsets[i]; int length = offsets[i + 1] - startIndex; // Load the glyph now, which starts at startIndex+objectOffset: results[i] = new CffSubPosition(startIndex + objectOffset, length); } return(results); }
private static Dictionary <int, List <int> > LoadDict(FontParser parser) { // How many are in here? int count = parser.ReadUInt16(); if (count == 0) { return(null); } // Read the offset size: int offsetSize = parser.ReadByte(); // Grab the position: int position = parser.Position; // Find where the data starts: int objectOffset = position + ((count + 1) * offsetSize) - 1; // Read two only: int firstOffset = parser.ReadOffset(offsetSize); int secondOffset = parser.ReadOffset(offsetSize); // Seek to the location: parser.Position = firstOffset + objectOffset; // Parse the dictionary now: Dictionary <int, List <int> > set = ParseCFFDict(parser, secondOffset - firstOffset); // Return: parser.Position = position; // Skip the rest: parser.Position += offsetSize * count; // Read the last offset: secondOffset = parser.ReadOffset(offsetSize); // Seek there, minus one as their not zero based: parser.Position += secondOffset - 1; return(set); }
private static void SkipIndex(FontParser parser) { // How many are in here? int count = parser.ReadUInt16(); if (count > 0) { // Read the offset size: int offsetSize = parser.ReadByte(); // Skip count offsets: parser.Position += offsetSize * count; // Read the last offset: int lastOffset = parser.ReadOffset(offsetSize); // Seek there, minus one as their not zero based: parser.Position += lastOffset - 1; } }
private static void SkipIndex(FontParser parser){ // How many are in here? int count=parser.ReadUInt16(); if(count>0){ // Read the offset size: int offsetSize=parser.ReadByte(); // Skip count offsets: parser.Position+=offsetSize*count; // Read the last offset: int lastOffset=parser.ReadOffset(offsetSize); // Seek there, minus one as their not zero based: parser.Position+=lastOffset-1; } }
private static void LoadGlyph(Glyph glyph, int contourCount, FontParser parser, float range) { // The contour count (tiny set): ushort[] endPointIndices = new ushort[contourCount]; // Load each endpoint: for (int i = 0; i < contourCount; i++) { endPointIndices[i] = parser.ReadUInt16(); } // How big is the instruction block? int instructionLength = parser.ReadUInt16(); // And skip it! parser.Position += instructionLength; // How many coordinates? int numberOfCoordinates = endPointIndices[endPointIndices.Length - 1] + 1; // Create the flag set: byte[] flags = new byte[numberOfCoordinates]; // For each one.. for (int i = 0; i < numberOfCoordinates; i++) { byte flag = parser.ReadByte(); flags[i] = flag; // If bit 3 is set, we repeat this flag n times, where n is the next byte. if ((flag & 8) > 0) { int repeatCount = parser.ReadByte(); for (int j = 0; j < repeatCount; j += 1) { i++; flags[i] = flag; } } } if (endPointIndices.Length > 0) { // X/Y coordinates are relative to the previous point, except for the first point which is relative to 0,0. if (numberOfCoordinates > 0) { // Current coord: int coord = 0; // Coord index: int coordIndex = 0; // The coord set: float[] coords = new float[numberOfCoordinates * 2]; // Load X coords: for (int i = 0; i < numberOfCoordinates; i++) { byte flag = flags[i]; coord = LoadGlyphCoordinate(parser, flag, coord, 2, 16); coords[coordIndex] = (float)coord / range; coordIndex += 2; } // Reset shared vars: coord = 0; coordIndex = 1; // Load Y coords: for (int i = 0; i < numberOfCoordinates; i++) { byte flag = flags[i]; coord = LoadGlyphCoordinate(parser, flag, coord, 4, 32); coords[coordIndex] = (float)coord / range; coordIndex += 2; } int[] orderedEnds = new int[endPointIndices.Length]; int currentEnd = 0; for (int i = 0; i < numberOfCoordinates; i++) { // Grab the flag: byte flag = flags[i]; // On curve flag - Control point otherwise: flag = (byte)(flag & 1); // Last point of the current contour? // For each end point index (tiny set - better than hash): for (int e = endPointIndices.Length - 1; e >= 0; e--) { if (endPointIndices[e] == i) { orderedEnds[currentEnd] = i; currentEnd++; break; } } // Update the flag - it's now just a 1 or 0: flags[i] = flag; } // Reset shared index again: coordIndex = 0; // Create our temp holders of point info: GlyphPoint firstPointRaw = new GlyphPoint(flags, coords); GlyphPoint lastPointRaw = new GlyphPoint(flags, coords); GlyphPoint prevPointRaw = new GlyphPoint(flags, coords); GlyphPoint currentPointRaw = new GlyphPoint(flags, coords); GlyphPoint controlPoint = new GlyphPoint(flags, coords); // For each contour.. for (int i = 0; i < contourCount; i++) { int pointOffset = 0; // Get the indices of the first/last points on this contour. int firstIndex = 0; int lastIndex = orderedEnds[i]; if (i != 0) { firstIndex = orderedEnds[i - 1] + 1; } GlyphPoint firstPoint = firstPointRaw; firstPoint.Set(firstIndex); GlyphPoint lastPoint = lastPointRaw; lastPoint.Set(lastIndex); if (firstPoint.OnCurve) { // No control point: controlPoint.Active = false; // The first point will be consumed by the moveTo command so skip it: pointOffset = 1; } else { if (lastPoint.OnCurve) { // If the first point is off-curve and the last point is on-curve, // start at the last point. firstPoint = lastPoint; } else { // If both first and last points are off-curve, start at their middle. firstPoint.X = (firstPoint.X + lastPoint.X) / 2f; firstPoint.Y = (firstPoint.Y + lastPoint.Y) / 2f; } controlPoint.Set(firstPoint); } glyph.MoveTo(firstPoint.X, firstPoint.Y); int contourStart = firstIndex + pointOffset; for (int j = contourStart; j <= lastIndex; j++) { // Setup the previous point: GlyphPoint prevPoint; if (j == firstIndex) { prevPoint = firstPoint; } else { prevPoint = prevPointRaw; prevPoint.Set(j - 1); } // Setup the current point: GlyphPoint pt = currentPointRaw; pt.Set(j); if (prevPoint.OnCurve && pt.OnCurve) { // Just a line here: glyph.LineTo(pt.X, pt.Y); } else if (prevPoint.OnCurve && !pt.OnCurve) { controlPoint.Set(pt); } else if (!prevPoint.OnCurve && !pt.OnCurve) { float midPointX = (prevPoint.X + pt.X) / 2f; float midPointY = (prevPoint.Y + pt.Y) / 2f; glyph.QuadraticCurveTo(prevPoint.X, prevPoint.Y, midPointX, midPointY); controlPoint.Set(pt); } else if (!prevPoint.OnCurve && pt.OnCurve) { // Previous point off-curve, this point on-curve. glyph.QuadraticCurveTo(controlPoint.X, controlPoint.Y, pt.X, pt.Y); controlPoint.Active = false; } } if (firstPoint != lastPoint) { // Close the path. if (controlPoint.Active) { // Still got a spare control point: glyph.QuadraticCurveTo(controlPoint.X, controlPoint.Y, firstPoint.X, firstPoint.Y); } // Just a normal close: glyph.ClosePath(); } } } } if (glyph.Font.WindingUnknown) { // Find the winding now: glyph.Font.FindWinding(glyph); } }
private static CffSubPosition[] LoadSubIndex(FontParser parser){ // How many are in here? int count=parser.ReadUInt16(); if(count==0){ return null; } // Create the offset set: int[] offsets=new int[count+1]; // Read the offset size: int offsetSize=parser.ReadByte(); // Read each offset: for(int i=0;i<=count;i++){ // Read the current offset: offsets[i]=parser.ReadOffset(offsetSize); } // Minus one as their not zero based: int objectOffset=parser.Position-1; // Seek over the table: parser.Position=objectOffset+offsets[count]; // Create the result set: CffSubPosition[] results=new CffSubPosition[offsets.Length-1]; // For each one.. for(int i=0;i<results.Length;i++){ // Get the (relative) indices: int startIndex=offsets[i]; int length=offsets[i+1]-startIndex; // Load the glyph now, which starts at startIndex+objectOffset: results[i]=new CffSubPosition(startIndex+objectOffset,length); } return results; }
private void Parse(int start, int codeLength) { // Seek there now: Parser.Position = start; // Where should the parser quit? int max = start + codeLength; float c1x; float c1y; float c2x; float c2y; int subIndex; CffSubPosition subCode; // For each bytecode.. while (Parser.Position < max) { // Grab the byte: byte v = Parser.ReadByte(); switch (v) { case 1: // hstem ParseStems(); break; case 3: // vstem ParseStems(); break; case 4: // vmoveto if (Stack.Length > 1 && !HasWidth) { Width = Stack.Shift() + NominalWidthX; HasWidth = true; } Y += Stack.Shift(); Glyph.ClosePath(); // Move: Glyph.MoveTo(X * ScaleRatio, Y * ScaleRatio); break; case 5: // rlineto while (Stack.Length > 0) { X += Stack.Shift(); Y += Stack.Shift(); Glyph.LineTo(X * ScaleRatio, Y * ScaleRatio); } break; case 6: // hlineto while (Stack.Length > 0) { X += Stack.Shift(); Glyph.LineTo(X * ScaleRatio, Y * ScaleRatio); if (Stack.Empty) { break; } Y += Stack.Shift(); Glyph.LineTo(X * ScaleRatio, Y * ScaleRatio); } break; case 7: // vlineto while (Stack.Length > 0) { Y += Stack.Shift(); Glyph.LineTo(X * ScaleRatio, Y * ScaleRatio); if (Stack.Length == 0) { break; } X += Stack.Shift(); Glyph.LineTo(X * ScaleRatio, Y * ScaleRatio); } break; case 8: // rrcurveto while (Stack.Length > 0) { c1x = X + Stack.Shift(); c1y = Y + Stack.Shift(); c2x = c1x + Stack.Shift(); c2y = c1y + Stack.Shift(); X = c2x + Stack.Shift(); Y = c2y + Stack.Shift(); Glyph.CurveTo(c1x * ScaleRatio, c1y * ScaleRatio, c2x * ScaleRatio, c2y * ScaleRatio, X * ScaleRatio, Y * ScaleRatio); } break; case 10: // callsubr subIndex = (int)Stack.Pop() + SubrsBias; subCode = Subrs[subIndex]; if (subCode != null) { // Cache the position: subIndex = Parser.Position; // Parse: Parse(subCode.Position, subCode.Length); // Re-apply: Parser.Position = subIndex; } break; case 11: // return return; case 12: // escape v = Parser.ReadByte(); break; case 14: // endchar if (Stack.Length > 0 && !HasWidth) { Width = Stack.Shift() + NominalWidthX; HasWidth = true; } // Close the glyph: Glyph.ClosePath(); break; case 18: // hstemhm ParseStems(); break; case 19: // hintmask case 20: // cntrmask ParseStems(); Parser.Position += (NStems + 7) >> 3; break; case 21: // rmoveto if (Stack.Length > 2 && !HasWidth) { Width = Stack.Shift() + NominalWidthX; HasWidth = true; } X += Stack.Shift(); Y += Stack.Shift(); Glyph.ClosePath(); // Move now: Glyph.MoveTo(X * ScaleRatio, Y * ScaleRatio); break; case 22: // hmoveto if (Stack.Length > 1 && !HasWidth) { Width = Stack.Shift() + NominalWidthX; HasWidth = true; } X += Stack.Shift(); Glyph.ClosePath(); // Move now: Glyph.MoveTo(X * ScaleRatio, Y * ScaleRatio); break; case 23: // vstemhm ParseStems(); break; case 24: // rcurveline while (Stack.Length > 2) { c1x = X + Stack.Shift(); c1y = Y + Stack.Shift(); c2x = c1x + Stack.Shift(); c2y = c1y + Stack.Shift(); X = c2x + Stack.Shift(); Y = c2y + Stack.Shift(); Glyph.CurveTo(c1x * ScaleRatio, c1y * ScaleRatio, c2x * ScaleRatio, c2y * ScaleRatio, X * ScaleRatio, Y * ScaleRatio); } X += Stack.Shift(); Y += Stack.Shift(); Glyph.LineTo(X * ScaleRatio, Y * ScaleRatio); break; case 25: // rlinecurve while (Stack.Length > 6) { X += Stack.Shift(); Y += Stack.Shift(); Glyph.LineTo(X * ScaleRatio, Y * ScaleRatio); } c1x = X + Stack.Shift(); c1y = Y + Stack.Shift(); c2x = c1x + Stack.Shift(); c2y = c1y + Stack.Shift(); X = c2x + Stack.Shift(); Y = c2y + Stack.Shift(); Glyph.CurveTo(c1x * ScaleRatio, c1y * ScaleRatio, c2x * ScaleRatio, c2y * ScaleRatio, X * ScaleRatio, Y * ScaleRatio); break; case 26: // vvcurveto if (Stack.IsOdd) { X += Stack.Shift(); } while (Stack.Length > 0) { c1x = X; c1y = Y + Stack.Shift(); c2x = c1x + Stack.Shift(); c2y = c1y + Stack.Shift(); X = c2x; Y = c2y + Stack.Shift(); Glyph.CurveTo(c1x * ScaleRatio, c1y * ScaleRatio, c2x * ScaleRatio, c2y * ScaleRatio, X * ScaleRatio, Y * ScaleRatio); } break; case 27: // hhcurveto if (Stack.IsOdd) { Y += Stack.Shift(); } while (Stack.Length > 0) { c1x = X + Stack.Shift(); c1y = Y; c2x = c1x + Stack.Shift(); c2y = c1y + Stack.Shift(); X = c2x + Stack.Shift(); Y = c2y; Glyph.CurveTo(c1x * ScaleRatio, c1y * ScaleRatio, c2x * ScaleRatio, c2y * ScaleRatio, X * ScaleRatio, Y * ScaleRatio); } break; case 28: // shortint Stack.Push(Parser.ReadInt16()); break; case 29: // callgsubr subIndex = (int)Stack.Pop() + GsubrsBias; subCode = GSubrs[subIndex]; if (subCode != null) { // Cache the position: subIndex = Parser.Position; // Parse: Parse(subCode.Position, subCode.Length); // Re-apply: Parser.Position = subIndex; } break; case 30: // vhcurveto while (Stack.Length > 0) { c1x = X; c1y = Y + Stack.Shift(); c2x = c1x + Stack.Shift(); c2y = c1y + Stack.Shift(); X = c2x + Stack.Shift(); Y = c2y + (Stack.Length == 1?Stack.Shift():0); Glyph.CurveTo(c1x * ScaleRatio, c1y * ScaleRatio, c2x * ScaleRatio, c2y * ScaleRatio, X * ScaleRatio, Y * ScaleRatio); if (Stack.Empty) { break; } c1x = X + Stack.Shift(); c1y = Y; c2x = c1x + Stack.Shift(); c2y = c1y + Stack.Shift(); Y = c2y + Stack.Shift(); X = c2x + (Stack.Length == 1?Stack.Shift():0); Glyph.CurveTo(c1x * ScaleRatio, c1y * ScaleRatio, c2x * ScaleRatio, c2y * ScaleRatio, X * ScaleRatio, Y * ScaleRatio); } break; case 31: // hvcurveto while (Stack.Length > 0) { c1x = X + Stack.Shift(); c1y = Y; c2x = c1x + Stack.Shift(); c2y = c1y + Stack.Shift(); Y = c2y + Stack.Shift(); X = c2x + (Stack.Length == 1 ? Stack.Shift() : 0); Glyph.CurveTo(c1x * ScaleRatio, c1y * ScaleRatio, c2x * ScaleRatio, c2y * ScaleRatio, X * ScaleRatio, Y * ScaleRatio); if (Stack.Empty) { break; } c1x = X; c1y = Y + Stack.Shift(); c2x = c1x + Stack.Shift(); c2y = c1y + Stack.Shift(); X = c2x + Stack.Shift(); Y = c2y + (Stack.Length == 1?Stack.Shift():0); Glyph.CurveTo(c1x * ScaleRatio, c1y * ScaleRatio, c2x * ScaleRatio, c2y * ScaleRatio, X * ScaleRatio, Y * ScaleRatio); } break; default: if (v < 32) { // Faulty operator. return; } else if (v < 247) { Stack.Push(v - 139); } else if (v < 251) { Stack.Push((v - 247) * 256 + Parser.ReadByte() + 108); } else if (v < 255) { Stack.Push(-(v - 251) * 256 - Parser.ReadByte() - 108); } else { Stack.Push((float)Parser.ReadInt32() / 65536f); } break; } } }
public static Dictionary<int,List<int>> ParseCFFDict(FontParser parser,int length){ // Create the results set: Dictionary<int,List<int>> results=new Dictionary<int,List<int>>(); // Our first values set: List<int> values=new List<int>(); // Note that rather awkwardly the key comes *after* the set of values. int max=parser.Position+length; // While there's more data.. while(parser.Position<max){ // Read the state byte: int state=parser.ReadByte(); if(state<22){ if(state==12){ // 2 byte key code. state=1200+parser.ReadByte(); } // Push values: results[state]=values; // Clear: values=new List<int>(); }else{ // Read the operand: int operand=ParseOperand(parser,state); // Push: values.Add(operand); } } return results; }
public static bool Load(int version, FontParser parser, FontFace font) { // Load the V1/V2 header: ushort numTables; LoadHeader(version, parser, out numTables); if (version == 1) { MemoryStream ms = new MemoryStream(parser.Data); // Get the ZLIB compression helper: Compressor zLib = Compression.Get("zlib"); // Read each table next. for (int i = 0; i < numTables; i++) { string tag = parser.ReadTag(); uint offset = parser.ReadUInt32(); uint compLength = parser.ReadUInt32(); uint origLength = parser.ReadUInt32(); parser.ReadUInt32(); // origChecksum // Cache position: int pos = parser.Position; if (compLength != origLength) { // Decompress the table now (zlib) // Seek to table: ms.Position = (int)offset; // Decompressed data: byte[] decompressedTable = new byte[(int)origLength]; // Decompress now into our target bytes: zLib.Decompress(ms, decompressedTable); } else { // Ordinary table. parser.HandleTable(tag, (int)offset, font); } // Restore position: parser.Position = pos; } } else if (version == 2) { // Read each table entry next. The data here is compressed as one single block after the table meta. Woff2Table[] tableHeaders = new Woff2Table[numTables]; int offset = 0; for (int i = 0; i < numTables; i++) { // Read the table entry: byte flags = parser.ReadByte(); string tag; int tagFlag = (flags & 63); if (tagFlag == 63) { tag = parser.ReadTag(); } else { tag = TagLookup[tagFlag]; } ulong origLength = parser.ReadBase128(); ulong transformLength = 0; int transformVersion = (flags >> 6); //0-3 if (tag == "glyf" || tag == "loca") { // transform length: transformLength = parser.ReadBase128(); } offset += (int)origLength; tableHeaders[i] = new Woff2Table(tag, offset, (int)transformLength, transformVersion); } } // All ok: return(true); }
public static bool Load(FontParser parser, int start, FontFace font, Glyph[] glyphs) { // Seek there: parser.Position = start; // Read the version (and check if it's zero): if (parser.ReadUInt16() != 0) { return(false); } // Strangely the cmap table can have lots of tables inside it. For now we're looking for the common type 3 table. // Number of tables: int tableCount = parser.ReadUInt16(); // Total characters in font: int characterCount = 0; int offset = -1; int favour = 0; for (int i = 0; i < tableCount; i += 1) { // Grab the platform ID: int platformId = parser.ReadUInt16(); // And the encoding ID: int encodingId = parser.ReadUInt16(); if (platformId == 3 || platformId == 0) { if (encodingId == 10) { // Top favourite - most broad Unicode encoding. // Read offset: offset = (int)parser.ReadUInt32(); break; } else if (encodingId == 1 || encodingId == 0) { // Read offset: offset = (int)parser.ReadUInt32(); // Mid-range favourite: favour = 1; continue; } else if (favour == 0) { // Anything else we'll give a try: // Read offset (but don't break): offset = (int)parser.ReadUInt32(); continue; } } // Skip offset: parser.Position += 4; } if (offset == -1) { // We don't support this font :( return(false); } // Seek to the cmap now: parser.Position = start + offset; // Check it's format 4: int format = parser.ReadUInt16(); if (format > 6) { // We now have e.g. 12.0 - another short here: parser.Position += 2; // Size/ structure are both 4 byte ints now: parser.Position += 8; } else { // Size of the sub-table (map length, u16): parser.Position += 2; // Structure of the sub-table (map language, u16): parser.Position += 2; } switch (format) { case 0: // Byte encoding table: for (int i = 0; i < 256; i++) { int rByte = parser.ReadByte(); Glyph glyph = glyphs[rByte]; if (glyph != null) { characterCount++; glyph.AddCharcode(i); } } break; case 2: // The offset to the headers: int subOffset = parser.Position + (256 * 2); // For each high byte: for (int i = 0; i < 256; i++) { // Read the index to the header and zero out the bottom 3 bits: int headerPosition = subOffset + (parser.ReadUInt16() & (~7)); // Read the header: int firstCode = parser.ReadUInt16(ref headerPosition); int entryCount = parser.ReadUInt16(ref headerPosition); short idDelta = parser.ReadInt16(ref headerPosition); // Grab the current position: int pos = headerPosition; // Read the idRangeOffset - the last part of the header: pos += parser.ReadUInt16(ref headerPosition); int maxCode = firstCode + entryCount; // Get the high byte value: int highByte = (i << 8); // For each low byte: for (int j = firstCode; j < maxCode; j++) { // Get the full charcode (which might not actually exist yet): int charCode = highByte + j; // Read the base of the glyphIndex: int p = parser.ReadUInt16(ref pos); if (p == 0) { continue; } p = (p + idDelta) & 0xFFFF; if (p == 0) { continue; } Glyph glyph = glyphs[p]; if (glyph != null) { characterCount++; glyph.AddCharcode(charCode); } } } break; case 4: // Segment count. It's doubled. int segCount = (parser.ReadUInt16() >> 1); // Search range, entry selector and range shift (don't need any): parser.Position += 6; int baseIndex = parser.Position; int endCountIndex = baseIndex; baseIndex += 2; int startCountIndex = baseIndex + segCount * 2; int idDeltaIndex = baseIndex + segCount * 4; int idRangeOffsetIndex = baseIndex + segCount * 6; for (int i = 0; i < segCount - 1; i++) { int endCount = parser.ReadUInt16(ref endCountIndex); int startCount = parser.ReadUInt16(ref startCountIndex); int idDelta = parser.ReadInt16(ref idDeltaIndex); int idRangeOffset = parser.ReadUInt16(ref idRangeOffsetIndex); for (int c = startCount; c <= endCount; c++) { int glyphIndex; if (idRangeOffset != 0) { // The idRangeOffset is relative to the current position in the idRangeOffset array. // Take the current offset in the idRangeOffset array. int glyphIndexOffset = (idRangeOffsetIndex - 2); // Add the value of the idRangeOffset, which will move us into the glyphIndex array. glyphIndexOffset += idRangeOffset; // Then add the character index of the current segment, multiplied by 2 for USHORTs. glyphIndexOffset += (c - startCount) * 2; glyphIndex = parser.ReadUInt16(ref glyphIndexOffset); if (glyphIndex != 0) { glyphIndex = (glyphIndex + idDelta) & 0xFFFF; } } else { glyphIndex = (c + idDelta) & 0xFFFF; } // Add a charcode to the glyph now: Glyph glyph = glyphs[glyphIndex]; if (glyph != null) { characterCount++; glyph.AddCharcode(c); } } } break; case 6: int firstCCode = parser.ReadUInt16(); int entryCCount = parser.ReadUInt16(); for (int i = 0; i < entryCCount; i++) { Glyph glyphC = glyphs[parser.ReadUInt16()]; if (glyphC != null) { characterCount++; glyphC.AddCharcode(firstCCode + i); } } break; case 12: int groups = (int)parser.ReadUInt32(); for (int i = 0; i < groups; i++) { int startCode = (int)parser.ReadUInt32(); int endCode = (int)parser.ReadUInt32(); int startGlyph = (int)parser.ReadUInt32(); int count = (endCode - startCode); for (int j = 0; j <= count; j++) { int glyphIndex = (startGlyph + j); Glyph glyph = glyphs[glyphIndex]; if (glyph != null) { characterCount++; glyph.AddCharcode(startCode + j); } } } break; default: Fonts.OnLogMessage("InfiniText does not currently support this font. If you need it, please contact us with this: Format: " + format); break; } font.CharacterCount = characterCount; return(true); }
public static int ParseOperand(FontParser parser,int b0){ if(b0==28){ return parser.ReadInt16(); } if(b0==29){ return parser.ReadInt32(); } if(b0==30){ // A floating point value which we really don't need - skipping! while(true){ byte b=parser.ReadByte(); int n1 = b >> 4; int n2 = b & 15; if (n1 ==15 || n2==15) { break; } } return 0; } if (b0 >= 32 && b0 <= 246) { return b0 - 139; } if (b0 >= 247 && b0 <= 250) { return (b0 - 247) * 256 + parser.ReadByte() + 108; } if (b0 >= 251 && b0 <= 254) { return -(b0 - 251) * 256 - parser.ReadByte() - 108; } return 0; }
private static int LoadSubTable(FontParser parser, int start, FontFace font, Glyph[] glyphs) { // Total characters in subtable: int characterCount = 0; // Seek to the cmap now: parser.Position = start; // Check it's format 4: int format = parser.ReadUInt16(); #if INFINITEXT_DEBUG Fonts.OnLogMessage("Cmap subtable format: " + format); #endif if (format > 13) { // We now have e.g. 14.0 - ulong here ("Length"): parser.Position += 4; } else if (format > 6) { // We now have e.g. 12.0 - another short here (reserved): parser.Position += 2; // Length and language are both 4 byte ints now: parser.Position += 8; } else { // Size of the sub-table (map length, u16): parser.Position += 2; // Structure of the sub-table (map language, u16): parser.Position += 2; } switch (format) { case 0: // Byte encoding table: for (int i = 0; i < 256; i++) { int rByte = parser.ReadByte(); Glyph glyph = glyphs[rByte]; if (glyph != null) { characterCount++; glyph.AddCharcode(i); } } break; case 2: // The offset to the headers: int subOffset = parser.Position + (256 * 2); // For each high byte: for (int i = 0; i < 256; i++) { // Read the index to the header and zero out the bottom 3 bits: int headerPosition = subOffset + (parser.ReadUInt16() & (~7)); // Read the header: int firstCode = parser.ReadUInt16(ref headerPosition); int entryCount = parser.ReadUInt16(ref headerPosition); short idDelta = parser.ReadInt16(ref headerPosition); // Grab the current position: int pos = headerPosition; // Read the idRangeOffset - the last part of the header: pos += parser.ReadUInt16(ref headerPosition); int maxCode = firstCode + entryCount; // Get the high byte value: int highByte = (i << 8); // For each low byte: for (int j = firstCode; j < maxCode; j++) { // Get the full charcode (which might not actually exist yet): int charCode = highByte + j; // Read the base of the glyphIndex: int p = parser.ReadUInt16(ref pos); if (p == 0) { continue; } p = (p + idDelta) & 0xFFFF; if (p == 0) { continue; } Glyph glyph = glyphs[p]; if (glyph != null) { characterCount++; glyph.AddCharcode(charCode); } } } break; case 4: // Segment count. It's doubled. int segCount = (parser.ReadUInt16() >> 1); // Search range, entry selector and range shift (don't need any): parser.Position += 6; int baseIndex = parser.Position; int endCountIndex = baseIndex; baseIndex += 2; int startCountIndex = baseIndex + segCount * 2; int idDeltaIndex = baseIndex + segCount * 4; int idRangeOffsetIndex = baseIndex + segCount * 6; for (int i = 0; i < segCount - 1; i++) { int endCount = parser.ReadUInt16(ref endCountIndex); int startCount = parser.ReadUInt16(ref startCountIndex); int idDelta = parser.ReadInt16(ref idDeltaIndex); int idRangeOffset = parser.ReadUInt16(ref idRangeOffsetIndex); for (int c = startCount; c <= endCount; c++) { int glyphIndex; if (idRangeOffset != 0) { // The idRangeOffset is relative to the current position in the idRangeOffset array. // Take the current offset in the idRangeOffset array. int glyphIndexOffset = (idRangeOffsetIndex - 2); // Add the value of the idRangeOffset, which will move us into the glyphIndex array. glyphIndexOffset += idRangeOffset; // Then add the character index of the current segment, multiplied by 2 for USHORTs. glyphIndexOffset += (c - startCount) * 2; glyphIndex = parser.ReadUInt16(ref glyphIndexOffset); if (glyphIndex != 0) { glyphIndex = (glyphIndex + idDelta) & 0xFFFF; } } else { glyphIndex = (c + idDelta) & 0xFFFF; } // Add a charcode to the glyph now: Glyph glyph = glyphs[glyphIndex]; if (glyph != null) { characterCount++; glyph.AddCharcode(c); } } } break; case 6: int firstCCode = parser.ReadUInt16(); int entryCCount = parser.ReadUInt16(); for (int i = 0; i < entryCCount; i++) { Glyph glyphC = glyphs[parser.ReadUInt16()]; if (glyphC != null) { characterCount++; glyphC.AddCharcode(firstCCode + i); } } break; case 10: // Trimmed array. Similar to format 6. int startCharCode = parser.ReadUInt16(); int numChars = parser.ReadUInt16(); for (int i = 0; i < numChars; i++) { Glyph glyphC = glyphs[parser.ReadUInt16()]; if (glyphC != null) { characterCount++; glyphC.AddCharcode(startCharCode + i); } } break; case 12: // Segmented coverage. // Mapping of 1 charcode to 1 glyph. "Segmented" because it can come in blocks called groups. int groups = (int)parser.ReadUInt32(); // For each group of glyphs.. for (int i = 0; i < groups; i++) { // Start/end charcode: int startCode = (int)parser.ReadUInt32(); int endCode = (int)parser.ReadUInt32(); // Start glyph ID: int startGlyph = (int)parser.ReadUInt32(); int count = (endCode - startCode); // For each glyph/charcode pair.. for (int j = 0; j <= count; j++) { // Get the glyph: int glyphIndex = (startGlyph + j); Glyph glyph = glyphs[glyphIndex]; if (glyph != null) { characterCount++; // Charcode is.. glyph.AddCharcode(startCode + j); } } } break; case 13: // Many to one. Same format as #12 but the meaning is a bit different. // How many groups? int glyphCount = (int)parser.ReadUInt32(); for (int i = 0; i < glyphCount; i++) { int startCode = (int)parser.ReadUInt32(); int endCode = (int)parser.ReadUInt32(); int glyphID = (int)parser.ReadUInt32(); // Get the glyph: Glyph glyph = glyphs[glyphID]; if (glyph != null) { int count = (endCode - startCode); // For each charcode.. for (int j = 0; j <= count; j++) { characterCount++; // Hook up glyph to this charcode: glyph.AddCharcode(startCode + j); } } } break; case 14: Fonts.OnLogMessage("InfiniText partially supports part of the font '" + font.Family.Name + "' - this is harmless. Search for this message for more."); // This font contains a format 14 CMAP Table. // Format 14 is "Unicode variation selectors" - essentially different versions of the same character. // E.g. a text Emoji character and a graphical one. // In a text system like InfiniText, that just means we must map a bunch of different charcodes // to the same glyph. // .. I Think! As usual, the OpenType spec doesn't make too much sense. // However, it appears to be entirely optional. // So, approx implementation is below, however getting the utf32 code point from the variation + base character // is completely undocumented - my best guess unfortunately threw errors. // See the commented out block below! break; /* * * case 14: * * // How many var selector records? * int records=(int)parser.ReadUInt32(); * * for(int i=0;i<records;i++){ * * // variation selector: * int varSelector=(int)parser.ReadUInt24(); * * // Offsets: * int defaultUVSOffset=(int)parser.ReadUInt32(); * int nonDefaultUVSOffset=(int)parser.ReadUInt32(); * * // Grab parser position: * int position=parser.Position; * * // Got a ref to a default style table? * if(defaultUVSOffset!=0){ * * // Yep! The UVS is simply a list of "base" characters, each with ranges of available charcodes. * // [BaseCharCode][The extended part. Each of these comes from the range.] * // The actual glyph is the one that we get by directly looking up each of the base characters. * * // Seek to the table: * parser.Position=start+defaultUVSOffset; * * // Read the unicode value ranges count: * int numUniRangesCount=(int)parser.ReadUInt32(); * * // For each one.. * for(int m=0;m<numUniRangesCount;m++){ * * // Read the base charcode: * int baseCharcode=(int)parser.ReadUInt24(); * * // Read the size of the range: * byte rangeSize=parser.ReadByte(); * * for(int c=0;c<=rangeSize;c++){ * * // Fetch the base glyph: * Glyph glyph=font.GetGlyphDirect(baseCharcode); * * if(glyph!=null){ * * // Combine baseCharcode with varSelector next to form the variation (of "glyph"). * * // Get the full charcode (this is incorrect!): * // int charcode=char.ConvertToUtf32((char)baseCharcode,(char)varSelector); * * // Add: * //glyph.AddCharcode(charcode); * * } * * // Move baseCharcode along: * baseCharcode++; * * } * * } * * // Restore parser: * parser.Position=position; * * } * * // Got a ref to a non-default style table? * if(nonDefaultUVSOffset!=0){ * * // Yep! The UVS is simply a list of "base" characters, each with ranges of available charcodes. * // [BaseCharCode][The extended part. Each of these comes from the range.] * // This time though, the glyph to use is directly specified * // (that's what gives it the "non-default" property). * * // Seek to the table: * parser.Position=start+nonDefaultUVSOffset; * * // Read the number of mappings: * int numMappings=(int)parser.ReadUInt32(); * * // For each one.. * for(int m=0;m<numMappings;m++){ * * // Read the base charcode: * int baseCharcode=(int)parser.ReadUInt24(); * * // Read glyph ID: * int glyphID=(int)parser.ReadUInt16(); * * // Get the glyph: * Glyph glyph=glyphs[glyphID]; * * if(glyph!=null){ * * // Combine baseCharcode with varSelector next to form the variation (of "glyph"). * * // Get the full charcode (this is incorrect!): * // int charcode=char.ConvertToUtf32((char)baseCharcode,(char)varSelector); * * // Add: * //glyph.AddCharcode(charcode); * * } * * } * * // Restore parser: * parser.Position=position; * * } * * } * * break; * */ default: Fonts.OnLogMessage("InfiniText does not currently support part of this font. If you need it, please contact us with this: Format: " + format); break; } return(characterCount); }
private static void LoadGlyph(Glyph glyph,int contourCount,FontParser parser,float range){ // The contour count (tiny set): ushort[] endPointIndices=new ushort[contourCount]; // Load each endpoint: for(int i=0;i<contourCount;i++){ endPointIndices[i]=parser.ReadUInt16(); } // How big is the instruction block? int instructionLength=parser.ReadUInt16(); // And skip it! parser.Position+=instructionLength; // How many coordinates? int numberOfCoordinates=endPointIndices[endPointIndices.Length-1]+1; // Create the flag set: byte[] flags=new byte[numberOfCoordinates]; // For each one.. for (int i = 0; i < numberOfCoordinates;i++) { byte flag=parser.ReadByte(); flags[i]=flag; // If bit 3 is set, we repeat this flag n times, where n is the next byte. if((flag&8)>0){ int repeatCount = parser.ReadByte(); for (int j = 0; j < repeatCount; j += 1) { i++; flags[i]=flag; } } } if (endPointIndices.Length > 0) { // X/Y coordinates are relative to the previous point, except for the first point which is relative to 0,0. if (numberOfCoordinates > 0){ // Current coord: int coord=0; // Coord index: int coordIndex=0; // The coord set: float[] coords=new float[numberOfCoordinates*2]; // Load X coords: for (int i = 0; i < numberOfCoordinates; i++) { byte flag = flags[i]; coord = LoadGlyphCoordinate(parser,flag,coord,2,16); coords[coordIndex]=(float)coord/range; coordIndex+=2; } // Reset shared vars: coord=0; coordIndex=1; // Load Y coords: for (int i = 0; i < numberOfCoordinates; i++) { byte flag = flags[i]; coord = LoadGlyphCoordinate(parser,flag,coord,4,32); coords[coordIndex]=(float)coord/range; coordIndex+=2; } int[] orderedEnds=new int[endPointIndices.Length]; int currentEnd=0; for (int i = 0; i < numberOfCoordinates; i++) { // Grab the flag: byte flag=flags[i]; // On curve flag - Control point otherwise: flag=(byte)(flag&1); // Last point of the current contour? // For each end point index (tiny set - better than hash): for(int e=endPointIndices.Length-1;e>=0;e--){ if(endPointIndices[e]==i){ orderedEnds[currentEnd]=i; currentEnd++; break; } } // Update the flag - it's now just a 1 or 0: flags[i]=flag; } // Reset shared index again: coordIndex=0; // Create our temp holders of point info: GlyphPoint firstPointRaw=new GlyphPoint(flags,coords); GlyphPoint lastPointRaw=new GlyphPoint(flags,coords); GlyphPoint prevPointRaw=new GlyphPoint(flags,coords); GlyphPoint currentPointRaw=new GlyphPoint(flags,coords); GlyphPoint controlPoint=new GlyphPoint(flags,coords); // For each contour.. for(int i=0;i<contourCount;i++){ int pointOffset=0; // Get the indices of the first/last points on this contour. int firstIndex=0; int lastIndex=orderedEnds[i]; if(i!=0){ firstIndex=orderedEnds[i-1]+1; } GlyphPoint firstPoint=firstPointRaw; firstPoint.Set(firstIndex); GlyphPoint lastPoint=lastPointRaw; lastPoint.Set(lastIndex); if(firstPoint.OnCurve){ // No control point: controlPoint.Active=false; // The first point will be consumed by the moveTo command so skip it: pointOffset=1; }else{ if(lastPoint.OnCurve){ // If the first point is off-curve and the last point is on-curve, // start at the last point. firstPoint=lastPoint; }else{ // If both first and last points are off-curve, start at their middle. firstPoint.X=(firstPoint.X+lastPoint.X)/2f; firstPoint.Y=(firstPoint.Y+lastPoint.Y)/2f; } controlPoint.Set(firstPoint); } glyph.MoveTo(firstPoint.X,firstPoint.Y); int contourStart=firstIndex+pointOffset; for(int j=contourStart;j<=lastIndex;j++){ // Setup the previous point: GlyphPoint prevPoint; if(j==firstIndex){ prevPoint=firstPoint; }else{ prevPoint=prevPointRaw; prevPoint.Set(j-1); } // Setup the current point: GlyphPoint pt=currentPointRaw; pt.Set(j); if(prevPoint.OnCurve && pt.OnCurve) { // Just a line here: glyph.LineTo(pt.X,pt.Y); }else if (prevPoint.OnCurve && !pt.OnCurve){ controlPoint.Set(pt); }else if(!prevPoint.OnCurve && !pt.OnCurve){ float midPointX=(prevPoint.X+pt.X)/2f; float midPointY=(prevPoint.Y+pt.Y)/2f; glyph.QuadraticCurveTo(prevPoint.X,prevPoint.Y,midPointX,midPointY); controlPoint.Set(pt); }else if(!prevPoint.OnCurve && pt.OnCurve){ // Previous point off-curve, this point on-curve. glyph.QuadraticCurveTo(controlPoint.X,controlPoint.Y,pt.X,pt.Y); controlPoint.Active=false; } } if(firstPoint!=lastPoint){ // Close the path. if(controlPoint.Active){ // Still got a spare control point: glyph.QuadraticCurveTo(controlPoint.X,controlPoint.Y,firstPoint.X,firstPoint.Y); } // Just a normal close: glyph.ClosePath(); } } } } if(glyph.Font.WindingUnknown){ // Find the winding now: glyph.Font.FindWinding(glyph); } }
public static int LoadGlyphCoordinate(FontParser parser,byte flag,int previousValue,int shortVectorBitMask,int sameBitMask){ int v; if((flag&shortVectorBitMask)>0){ // The coordinate is 1 byte long. v = parser.ReadByte(); // The "same" bit is re-used for short values to signify the sign of the value. if ((flag & sameBitMask) == 0) { v = -v; } v = previousValue + v; } else { // The coordinate is 2 bytes long. // If the same bit is set, the coordinate is the same as the previous coordinate. if ((flag & sameBitMask) > 0) { v = previousValue; } else { // Parse the coordinate as a signed 16-bit delta value. v = previousValue + parser.ReadInt16(); } } return v; }
public static Glyph ParseGlyph(FontParser parser,FontFace font,float range){ // How many contours has it got? int contourCount=parser.ReadInt16(); // Skip bounds - we don't trust these too much, so we'll figure them out ourselves: parser.Position+=8; if(contourCount>0){ // This glyph is not a composite. // Create the glyph: Glyph glyph=new Glyph(font); if(Fonts.Preload){ LoadGlyph(glyph,contourCount,parser,range); }else{ // Increase unloaded count: font.UnloadedGlyphs++; // Add position info: glyph.AddPathNode(new LoadMetaPoint(parser.Position,contourCount)); } return glyph; }else if(contourCount==0){ // Empty glyph e.g. space. Create the glyph: Glyph glyph=new Glyph(font); return glyph; } CompositeGlyph compGlyph=new CompositeGlyph(font); bool moreComponents=true; while(moreComponents){ ushort cFlags=parser.ReadUInt16(); ushort glyphIndex=parser.ReadUInt16(); VectorTransform component=new VectorTransform(glyphIndex); if ((cFlags & 1) > 0) { // The arguments are words component.Dx = (float)parser.ReadInt16() / range; component.Dy = (float)parser.ReadInt16() / range; } else { // The arguments are bytes component.Dx = (float)parser.ReadByte() / range; component.Dy = (float)parser.ReadByte() / range; } if ((cFlags & 8) > 0) { // We have one scale component.XScale = component.YScale = parser.ReadF2Dot14(); } else if ((cFlags & 64) > 0) { // We have an X / Y scale component.XScale = parser.ReadF2Dot14(); component.YScale = parser.ReadF2Dot14(); } else if ((cFlags & 128) > 0) { // We have a 2x2 transformation component.XScale = parser.ReadF2Dot14(); component.Scale01 = parser.ReadF2Dot14(); component.Scale10 = parser.ReadF2Dot14(); component.YScale = parser.ReadF2Dot14(); } // Push the component to the end: compGlyph.AddComponent(component); moreComponents = ((cFlags & 32)==32); } return compGlyph; }
public static bool Load(FontParser parser,int start,FontFace font,Glyph[] glyphs){ // Seek there: parser.Position=start; // Read the version (and check if it's zero): if(parser.ReadUInt16()!=0){ return false; } // Strangely the cmap table can have lots of tables inside it. For now we're looking for the common type 3 table. // Number of tables: int tableCount=parser.ReadUInt16(); // Total characters in font: int characterCount=0; int offset=-1; int favour=0; for(int i = 0; i < tableCount; i += 1) { // Grab the platform ID: int platformId=parser.ReadUInt16(); // And the encoding ID: int encodingId=parser.ReadUInt16(); if(platformId==3 || platformId==0){ if(encodingId==10){ // Top favourite - most broad Unicode encoding. // Read offset: offset=(int)parser.ReadUInt32(); break; }else if(encodingId==1 || encodingId==0){ // Read offset: offset=(int)parser.ReadUInt32(); // Mid-range favourite: favour=1; continue; }else if(favour==0){ // Anything else we'll give a try: // Read offset (but don't break): offset=(int)parser.ReadUInt32(); continue; } } // Skip offset: parser.Position+=4; } if(offset==-1){ // We don't support this font :( return false; } // Seek to the cmap now: parser.Position=start+offset; // Check it's format 4: int format=parser.ReadUInt16(); if(format>6){ // We now have e.g. 12.0 - another short here: parser.Position+=2; // Size/ structure are both 4 byte ints now: parser.Position+=8; }else{ // Size of the sub-table (map length, u16): parser.Position+=2; // Structure of the sub-table (map language, u16): parser.Position+=2; } switch(format){ case 0: // Byte encoding table: for(int i=0;i<256;i++){ int rByte=parser.ReadByte(); Glyph glyph=glyphs[rByte]; if(glyph!=null){ characterCount++; glyph.AddCharcode(i); } } break; case 2: // The offset to the headers: int subOffset=parser.Position + (256 * 2); // For each high byte: for(int i=0;i<256;i++){ // Read the index to the header and zero out the bottom 3 bits: int headerPosition=subOffset + (parser.ReadUInt16() & (~7)); // Read the header: int firstCode=parser.ReadUInt16(ref headerPosition); int entryCount=parser.ReadUInt16(ref headerPosition); short idDelta=parser.ReadInt16(ref headerPosition); // Grab the current position: int pos=headerPosition; // Read the idRangeOffset - the last part of the header: pos+=parser.ReadUInt16(ref headerPosition); int maxCode=firstCode+entryCount; // Get the high byte value: int highByte=(i<<8); // For each low byte: for (int j=firstCode;j<maxCode;j++){ // Get the full charcode (which might not actually exist yet): int charCode=highByte+j; // Read the base of the glyphIndex: int p=parser.ReadUInt16(ref pos); if(p==0){ continue; } p=(p+idDelta) & 0xFFFF; if(p==0){ continue; } Glyph glyph=glyphs[p]; if(glyph!=null){ characterCount++; glyph.AddCharcode(charCode); } } } break; case 4: // Segment count. It's doubled. int segCount=(parser.ReadUInt16() >> 1); // Search range, entry selector and range shift (don't need any): parser.Position+=6; int baseIndex=parser.Position; int endCountIndex=baseIndex; baseIndex+=2; int startCountIndex = baseIndex + segCount * 2; int idDeltaIndex = baseIndex + segCount * 4; int idRangeOffsetIndex = baseIndex + segCount * 6; for(int i = 0; i < segCount - 1; i ++){ int endCount = parser.ReadUInt16(ref endCountIndex); int startCount = parser.ReadUInt16(ref startCountIndex); int idDelta = parser.ReadInt16(ref idDeltaIndex); int idRangeOffset = parser.ReadUInt16(ref idRangeOffsetIndex); for(int c = startCount; c <= endCount;c++){ int glyphIndex; if(idRangeOffset != 0){ // The idRangeOffset is relative to the current position in the idRangeOffset array. // Take the current offset in the idRangeOffset array. int glyphIndexOffset = (idRangeOffsetIndex - 2); // Add the value of the idRangeOffset, which will move us into the glyphIndex array. glyphIndexOffset += idRangeOffset; // Then add the character index of the current segment, multiplied by 2 for USHORTs. glyphIndexOffset += (c - startCount) * 2; glyphIndex=parser.ReadUInt16(ref glyphIndexOffset); if(glyphIndex!=0){ glyphIndex = (glyphIndex + idDelta) & 0xFFFF; } }else{ glyphIndex = (c + idDelta) & 0xFFFF; } // Add a charcode to the glyph now: Glyph glyph=glyphs[glyphIndex]; if(glyph!=null){ characterCount++; glyph.AddCharcode(c); } } } break; case 6: int firstCCode=parser.ReadUInt16(); int entryCCount=parser.ReadUInt16(); for(int i=0;i<entryCCount;i++){ Glyph glyphC=glyphs[parser.ReadUInt16()]; if(glyphC!=null){ characterCount++; glyphC.AddCharcode(firstCCode+i); } } break; case 12: int groups=(int)parser.ReadUInt32(); for(int i=0;i<groups;i++){ int startCode=(int)parser.ReadUInt32(); int endCode=(int)parser.ReadUInt32(); int startGlyph=(int)parser.ReadUInt32(); int count=(endCode - startCode); for(int j=0;j<=count;j++){ int glyphIndex=(startGlyph+j); Glyph glyph=glyphs[glyphIndex]; if(glyph!=null){ characterCount++; glyph.AddCharcode(startCode+j); } } } break; default: Fonts.OnLogMessage("InfiniText does not currently support this font. If you need it, please contact us with this: Format: "+format); break; } font.CharacterCount=characterCount; return true; }
private static Dictionary<int,List<int>> LoadDict(FontParser parser){ // How many are in here? int count=parser.ReadUInt16(); if(count==0){ return null; } // Read the offset size: int offsetSize=parser.ReadByte(); // Grab the position: int position=parser.Position; // Find where the data starts: int objectOffset=position+((count+1)*offsetSize)-1; // Read two only: int firstOffset=parser.ReadOffset(offsetSize); int secondOffset=parser.ReadOffset(offsetSize); // Seek to the location: parser.Position=firstOffset+objectOffset; // Parse the dictionary now: Dictionary<int,List<int>> set=ParseCFFDict(parser,secondOffset-firstOffset); // Return: parser.Position=position; // Skip the rest: parser.Position+=offsetSize*count; // Read the last offset: secondOffset=parser.ReadOffset(offsetSize); // Seek there, minus one as their not zero based: parser.Position+=secondOffset-1; return set; }
public static Glyph ParseGlyph(FontParser parser, FontFace font, float range) { // How many contours has it got? int contourCount = parser.ReadInt16(); // Skip bounds - we don't trust these too much, so we'll figure them out ourselves: parser.Position += 8; if (contourCount > 0) { // This glyph is not a composite. // Create the glyph: Glyph glyph = new Glyph(font); if (Fonts.Preload) { LoadGlyph(glyph, contourCount, parser, range); } else { // Increase unloaded count: font.UnloadedGlyphs++; // Add position info: glyph.AddPathNode(new LoadMetaPoint(parser.Position, contourCount)); } return(glyph); } else if (contourCount == 0) { // Empty glyph e.g. space. Create the glyph: Glyph glyph = new Glyph(font); return(glyph); } CompositeGlyph compGlyph = new CompositeGlyph(font); bool moreComponents = true; while (moreComponents) { ushort cFlags = parser.ReadUInt16(); ushort glyphIndex = parser.ReadUInt16(); VectorTransform component = new VectorTransform(glyphIndex); if ((cFlags & 1) > 0) { // The arguments are words component.Dx = (float)parser.ReadInt16() / range; component.Dy = (float)parser.ReadInt16() / range; } else { // The arguments are bytes component.Dx = (float)parser.ReadByte() / range; component.Dy = (float)parser.ReadByte() / range; } if ((cFlags & 8) > 0) { // We have one scale component.XScale = component.YScale = parser.ReadF2Dot14(); } else if ((cFlags & 64) > 0) { // We have an X / Y scale component.XScale = parser.ReadF2Dot14(); component.YScale = parser.ReadF2Dot14(); } else if ((cFlags & 128) > 0) { // We have a 2x2 transformation component.XScale = parser.ReadF2Dot14(); component.Scale01 = parser.ReadF2Dot14(); component.Scale10 = parser.ReadF2Dot14(); component.YScale = parser.ReadF2Dot14(); } // Push the component to the end: compGlyph.AddComponent(component); moreComponents = ((cFlags & 32) == 32); } return(compGlyph); }