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 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; } } }
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); } }