//Apache2, 2018, apache/pdfbox Authors ( https://github.com/apache/pdfbox) //MIT, 2018-present, WinterDev using System; using System.Collections.Generic; using System.Diagnostics; namespace Typography.OpenFont.CFF { public class CffEvaluationEngine { float _scale = 1;//default readonly Stack _evalStackPool = new Stack(); class PxScaleGlyphTx : IGlyphTranslator { readonly float _scale; readonly IGlyphTranslator _tx; bool _is_contour_opened; public PxScaleGlyphTx(float scale, IGlyphTranslator tx) { _scale = scale; _tx = tx; } public void BeginRead(int contourCount) { _tx.BeginRead(contourCount); } public void CloseContour() { _is_contour_opened = false; _tx.CloseContour(); } public void Curve3(float x1, float y1, float x2, float y2) { _is_contour_opened = true; _tx.Curve3(x1 * _scale, y1 * _scale, x2 * _scale, y2 * _scale); } public void Curve4(float x1, float y1, float x2, float y2, float x3, float y3) { _is_contour_opened = true; _tx.Curve4(x1 * _scale, y1 * _scale, x2 * _scale, y2 * _scale, x3 * _scale, y3 * _scale); } public void EndRead() { _tx.EndRead(); } public void LineTo(float x1, float y1) { _is_contour_opened = true; _tx.LineTo(x1 * _scale, y1 * _scale); } public void MoveTo(float x0, float y0) { _tx.MoveTo(x0 * _scale, y0 * _scale); } // public bool IsContourOpened => _is_contour_opened; } public CffEvaluationEngine() { } public void Run(IGlyphTranslator tx, Cff1GlyphData glyphData, float scale = 1) { Run(tx, glyphData.GlyphInstructions, scale); } internal void Run(IGlyphTranslator tx, Type2Instruction[] instructionList, float scale = 1) { //all fields are set to new values*** _scale = scale; double currentX = 0, currentY = 0; var scaleTx = new PxScaleGlyphTx(scale, tx); // scaleTx.BeginRead(0);//unknown contour count // Run(scaleTx, instructionList, ref currentX, ref currentY); // // //some cff end without closing the latest contour? if (scaleTx.IsContourOpened) { scaleTx.CloseContour(); } scaleTx.EndRead(); } void Run(IGlyphTranslator tx, Type2Instruction[] instructionList, ref double currentX, ref double currentY) { //recursive *** Type2EvaluationStack evalStack = GetFreeEvalStack(); //** #if DEBUG //evalStack.dbugGlyphIndex = instructionList.dbugGlyphIndex; #endif evalStack._currentX = currentX; evalStack._currentY = currentY; evalStack.GlyphTranslator = tx; for (int i = 0; i < instructionList.Length; ++i) { Type2Instruction inst = instructionList[i]; //---------- //this part is our extension to the original int merge_flags = inst.Op >> 6;//upper 2 bits is our extension flags switch (merge_flags) { case 0: //nothing break; case 1: evalStack.Push(inst.Value); break; case 2: evalStack.Push((short)(inst.Value >> 16)); evalStack.Push((short)(inst.Value >> 0)); break; case 3: evalStack.Push((sbyte)(inst.Value >> 24)); evalStack.Push((sbyte)(inst.Value >> 16)); evalStack.Push((sbyte)(inst.Value >> 8)); evalStack.Push((sbyte)(inst.Value >> 0)); break; } //---------- switch ((OperatorName)((inst.Op & 0b111111)))//we use only 6 lower bits for op_name { default: throw new OpenFontNotSupportedException(); case OperatorName.GlyphWidth: //TODO: break; case OperatorName.LoadInt: evalStack.Push(inst.Value); break; case OperatorName.LoadSbyte4: //4 consecutive sbyte evalStack.Push((sbyte)(inst.Value >> 24)); evalStack.Push((sbyte)(inst.Value >> 16)); evalStack.Push((sbyte)(inst.Value >> 8)); evalStack.Push((sbyte)(inst.Value >> 0)); break; case OperatorName.LoadSbyte3: evalStack.Push((sbyte)(inst.Value >> 24)); evalStack.Push((sbyte)(inst.Value >> 16)); evalStack.Push((sbyte)(inst.Value >> 8)); break; case OperatorName.LoadShort2: evalStack.Push((short)(inst.Value >> 16)); evalStack.Push((short)(inst.Value >> 0)); break; case OperatorName.LoadFloat: evalStack.Push(inst.ReadValueAsFixed1616()); break; case OperatorName.endchar: evalStack.EndChar(); break; case OperatorName.flex: evalStack.Flex(); break; case OperatorName.hflex: evalStack.H_Flex(); break; case OperatorName.hflex1: evalStack.H_Flex1(); break; case OperatorName.flex1: evalStack.Flex1(); break; //------------------------- //4.4: Arithmetic Operators case OperatorName.abs: evalStack.Op_Abs(); break; case OperatorName.add: evalStack.Op_Add(); break; case OperatorName.sub: evalStack.Op_Sub(); break; case OperatorName.div: evalStack.Op_Div(); break; case OperatorName.neg: evalStack.Op_Neg(); break; case OperatorName.random: evalStack.Op_Random(); break; case OperatorName.mul: evalStack.Op_Mul(); break; case OperatorName.sqrt: evalStack.Op_Sqrt(); break; case OperatorName.drop: evalStack.Op_Drop(); break; case OperatorName.exch: evalStack.Op_Exch(); break; case OperatorName.index: evalStack.Op_Index(); break; case OperatorName.roll: evalStack.Op_Roll(); break; case OperatorName.dup: evalStack.Op_Dup(); break; //------------------------- //4.5: Storage Operators case OperatorName.put: evalStack.Put(); break; case OperatorName.get: evalStack.Get(); break; //------------------------- //4.6: Conditional case OperatorName.and: evalStack.Op_And(); break; case OperatorName.or: evalStack.Op_Or(); break; case OperatorName.not: evalStack.Op_Not(); break; case OperatorName.eq: evalStack.Op_Eq(); break; case OperatorName.ifelse: evalStack.Op_IfElse(); break; // case OperatorName.rlineto: evalStack.R_LineTo(); break; case OperatorName.hlineto: evalStack.H_LineTo(); break; case OperatorName.vlineto: evalStack.V_LineTo(); break; case OperatorName.rrcurveto: evalStack.RR_CurveTo(); break; case OperatorName.hhcurveto: evalStack.HH_CurveTo(); break; case OperatorName.hvcurveto: evalStack.HV_CurveTo(); break; case OperatorName.rcurveline: evalStack.R_CurveLine(); break; case OperatorName.rlinecurve: evalStack.R_LineCurve(); break; case OperatorName.vhcurveto: evalStack.VH_CurveTo(); break; case OperatorName.vvcurveto: evalStack.VV_CurveTo(); break; //------------------------------------------------------------------- case OperatorName.rmoveto: evalStack.R_MoveTo(); break; case OperatorName.hmoveto: evalStack.H_MoveTo(); break; case OperatorName.vmoveto: evalStack.V_MoveTo(); break; //------------------------------------------------------------------- //4.3 Hint Operators case OperatorName.hstem: evalStack.H_Stem(); break; case OperatorName.vstem: evalStack.V_Stem(); break; case OperatorName.vstemhm: evalStack.V_StemHM(); break; case OperatorName.hstemhm: evalStack.H_StemHM(); break; //-------------------------- case OperatorName.hintmask1: evalStack.HintMask1(inst.Value); break; case OperatorName.hintmask2: evalStack.HintMask2(inst.Value); break; case OperatorName.hintmask3: evalStack.HintMask3(inst.Value); break; case OperatorName.hintmask4: evalStack.HintMask4(inst.Value); break; case OperatorName.hintmask_bits: evalStack.HintMaskBits(inst.Value); break; //------------------------------ case OperatorName.cntrmask1: evalStack.CounterSpaceMask1(inst.Value); break; case OperatorName.cntrmask2: evalStack.CounterSpaceMask2(inst.Value); break; case OperatorName.cntrmask3: evalStack.CounterSpaceMask3(inst.Value); break; case OperatorName.cntrmask4: evalStack.CounterSpaceMask4(inst.Value); break; case OperatorName.cntrmask_bits: evalStack.CounterSpaceMaskBits(inst.Value); break; //------------------------- //4.7: Subroutine Operators case OperatorName._return: { //*** //don't forget to return _evalStack's currentX, currentY to prev evl context currentX = evalStack._currentX; currentY = evalStack._currentY; evalStack.Ret(); } break; //should not occur!-> since we replace this in parsing step case OperatorName.callgsubr: case OperatorName.callsubr: throw new OpenFontNotSupportedException(); } } ReleaseEvalStack(evalStack);//**** } Type2EvaluationStack GetFreeEvalStack() { if (_evalStackPool.Count > 0) { return _evalStackPool.Pop(); } else { return new Type2EvaluationStack(); } } void ReleaseEvalStack(Type2EvaluationStack evalStack) { evalStack.Reset(); _evalStackPool.Push(evalStack); } } class Type2EvaluationStack { internal double _currentX; internal double _currentY; double[] _argStack = new double[50]; int _currentIndex = 0; //current stack index IGlyphTranslator _glyphTranslator; #if DEBUG public int dbugGlyphIndex; #endif public Type2EvaluationStack() { } public void Reset() { _currentIndex = 0; _currentX = _currentY = 0; _glyphTranslator = null; } public IGlyphTranslator GlyphTranslator { get => _glyphTranslator; set => _glyphTranslator = value; } public void Push(double value) { _argStack[_currentIndex] = value; _currentIndex++; } public void Push(int value) { _argStack[_currentIndex] = value; _currentIndex++; } //Many operators take their arguments from the bottom-most //entries in the Type 2 argument stack; this behavior is indicated //by the stack bottom symbol ‘| -’ appearing to the left of the first //argument.Operators that clear the argument stack are //indicated by the stack bottom symbol ‘| -’ in the result position //of the operator definition //[NOTE4]: //The first stack - clearing operator, which must be one of... //hstem, hstemhm, vstem, vstemhm, cntrmask, //hintmask, hmoveto, vmoveto, rmoveto, or endchar, //... //takes an additional argument — the width(as //described earlier), which may be expressed as zero or one numeric //argument //------------------------- //4.1: Path Construction Operators /// /// rmoveto /// public void R_MoveTo() { //|- dx1 dy1 rmoveto(21) |- //moves the current point to //a position at the relative coordinates(dx1, dy1) //see [NOTE4] #if DEBUG if (_currentIndex != 2) { throw new OpenFontNotSupportedException(); } #endif _glyphTranslator.CloseContour(); _glyphTranslator.MoveTo((float)(_currentX += _argStack[0]), (float)(_currentY += _argStack[1])); _currentIndex = 0; //clear stack } /// /// hmoveto /// public void H_MoveTo() { //|- dx1 hmoveto(22) |- //moves the current point //dx1 units in the horizontal direction //see [NOTE4] _glyphTranslator.MoveTo((float)(_currentX += _argStack[0]), (float)_currentY); _currentIndex = 0; //clear stack } public void V_MoveTo() { //|- dy1 vmoveto (4) |- //moves the current point //dy1 units in the vertical direction. //see [NOTE4] #if DEBUG if (_currentIndex > 1) { throw new OpenFontNotSupportedException(); } #endif _glyphTranslator.MoveTo((float)_currentX, (float)(_currentY += _argStack[0])); _currentIndex = 0; //clear stack } public void R_LineTo() { //|- {dxa dya}+ rlineto (5) |- //appends a line from the current point to //a position at the relative coordinates dxa, dya. //Additional rlineto operations are //performed for all subsequent argument pairs. //The number of //lines is determined from the number of arguments on the stack #if DEBUG if ((_currentIndex % 2) != 0) { throw new OpenFontNotSupportedException(); } #endif for (int i = 0; i < _currentIndex;) { _glyphTranslator.LineTo((float)(_currentX += _argStack[i]), (float)(_currentY += _argStack[i + 1])); i += 2; } _currentIndex = 0; //clear stack } public void H_LineTo() { //|- dx1 {dya dxb}* hlineto (6) |- //|- {dxa dyb}+ hlineto (6) |- //appends a horizontal line of length //dx1 to the current point. //With an odd number of arguments, subsequent argument pairs //are interpreted as alternating values of //dy and dx, for which additional lineto //operators draw alternating vertical and //horizontal lines. //With an even number of arguments, the //arguments are interpreted as alternating horizontal and //vertical lines. The number of lines is determined from the //number of arguments on the stack. //first elem int i = 0; if ((_currentIndex % 2) != 0) { //|- dx1 {dya dxb}* hlineto (6) |- //odd number _glyphTranslator.LineTo((float)(_currentX += _argStack[i]), (float)_currentY); i++; for (; i < _currentIndex;) { _glyphTranslator.LineTo((float)(_currentX), (float)(_currentY += _argStack[i])); _glyphTranslator.LineTo((float)(_currentX += _argStack[i + 1]), (float)(_currentY)); i += 2; } } else { //even number //|- {dxa dyb}+ hlineto (6) |- for (; i < _currentIndex;) { //line to _glyphTranslator.LineTo((float)(_currentX += _argStack[i]), (float)(_currentY)); _glyphTranslator.LineTo((float)(_currentX), (float)(_currentY += _argStack[i + 1])); // i += 2; } } _currentIndex = 0; //clear stack } public void V_LineTo() { //|- dy1 {dxa dyb}* vlineto (7) |- //|- {dya dxb}+ vlineto (7) |- //appends a vertical line of length //dy1 to the current point. //With an odd number of arguments, subsequent argument pairs are //interpreted as alternating values of dx and dy, for which additional //lineto operators draw alternating horizontal and //vertical lines. //With an even number of arguments, the //arguments are interpreted as alternating vertical and //horizontal lines. The number of lines is determined from the //number of arguments on the stack. //first elem int i = 0; if ((_currentIndex % 2) != 0) { //|- dy1 {dxa dyb}* vlineto (7) |- //odd number _glyphTranslator.LineTo((float)_currentX, (float)(_currentY += _argStack[i])); i++; for (; i < _currentIndex;) { //line to _glyphTranslator.LineTo((float)(_currentX += _argStack[i]), (float)(_currentY)); _glyphTranslator.LineTo((float)(_currentX), (float)(_currentY += _argStack[i + 1])); // i += 2; } } else { //even number //|- {dya dxb}+ vlineto (7) |- for (; i < _currentIndex;) { //line to _glyphTranslator.LineTo((float)(_currentX), (float)(_currentY += _argStack[i])); _glyphTranslator.LineTo((float)(_currentX += _argStack[i + 1]), (float)(_currentY)); // i += 2; } } _currentIndex = 0; //clear stack } public void RR_CurveTo() { //|- {dxa dya dxb dyb dxc dyc}+ rrcurveto (8) |- //appends a Bézier curve, defined by dxa...dyc, to the current point. //For each subsequent set of six arguments, an additional //curve is appended to the current point. //The number of curve segments is determined from //the number of arguments on the number stack and //is limited only by the size of the number stack //All Bézier curve path segments are drawn using six arguments, //dxa, dya, dxb, dyb, dxc, dyc; where dxa and dya are relative to //the current point, and all subsequent arguments are relative to //the previous point.A number of the curve operators take //advantage of the situation where some tangent points are //horizontal or vertical(and hence the value is zero), thus //reducing the number of arguments needed. int i = 0; #if DEBUG if ((_currentIndex % 6) != 0) { throw new OpenFontNotSupportedException(); } #endif double curX = _currentX; double curY = _currentY; for (; i < _currentIndex;) { _glyphTranslator.Curve4( (float)(curX += _argStack[i + 0]), (float)(curY += _argStack[i + 1]), //dxa,dya (float)(curX += _argStack[i + 2]), (float)(curY += _argStack[i + 3]), //dxb,dyb (float)(curX += _argStack[i + 4]), (float)(curY += _argStack[i + 5]) //dxc,dyc ); // i += 6; } _currentX = curX; _currentY = curY; _currentIndex = 0; //clear stack } public void HH_CurveTo() { //|- dy1? {dxa dxb dyb dxc}+ hhcurveto (27) |- //appends one or more Bézier curves, as described by the //dxa...dxc set of arguments, to the current point. //For each curve, if there are 4 arguments, //the curve starts and ends horizontal. //The first curve need not start horizontal (the odd argument //case). Note the argument order for the odd argument case int i = 0; int count = _currentIndex; double curX = _currentX; double curY = _currentY; if ((count % 2) != 0) { //odd number _glyphTranslator.Curve4( (float)(curX += _argStack[1]), (float)(curY += _argStack[0]), //dxa,+dy1? (float)(curX += _argStack[2]), (float)(curY += _argStack[3]), //dxb,dyb (float)(curX += _argStack[4]), (float)(curY) //dxc,+0 ); i += 5; count -= 5; } //next while (count > 0) { _glyphTranslator.Curve4( (float)(curX += _argStack[i + 0]), (float)(curY), //dxa,+0 (float)(curX += _argStack[i + 1]), (float)(curY += _argStack[i + 2]), //dxb,dyb (float)(curX += _argStack[i + 3]), (float)(curY) //dxc,+0 ); // i += 4; count -= 4; } _currentX = curX; _currentY = curY; _currentIndex = 0; //clear stack } public void HV_CurveTo() { //|- dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf? hvcurveto (31) |- //|- {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf? hvcurveto (31) |- //appends one or more Bézier curves to the current point. //The tangent for the first Bézier must be horizontal, and the second //must be vertical (except as noted below). //If there is a multiple of four arguments, the curve starts //horizontal and ends vertical.Note that the curves alternate //between start horizontal, end vertical, and start vertical, and //end horizontal.The last curve(the odd argument case) need not //end horizontal/ vertical. #if DEBUG #endif int i = 0; int remainder = 0; switch (remainder = (_currentIndex % 8)) { default: throw new OpenFontNotSupportedException(); case 0: case 1: { //|- {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf? hvcurveto (31) |- double curX = _currentX; double curY = _currentY; int count = _currentIndex; while (count > 0) { _glyphTranslator.Curve4( (float)(curX += _argStack[i + 0]), (float)(curY), //+dxa,0 (float)(curX += _argStack[i + 1]), (float)(curY += _argStack[i + 2]), //dxb,dyb (float)(curX), (float)(curY += _argStack[i + 3]) //+0,dyc ); if (count == 9) { //last cycle _glyphTranslator.Curve4( (float)(curX), (float)(curY += _argStack[i + 4]), //+0,dyd (float)(curX += _argStack[i + 5]), (float)(curY += _argStack[i + 6]), //dxe,dye (float)(curX += _argStack[i + 7]), (float)(curY += _argStack[i + 8]) //dxf,+dyf ); // count -= 9; i += 9; } else { _glyphTranslator.Curve4( (float)(curX), (float)(curY += _argStack[i + 4]), //+0,dyd (float)(curX += _argStack[i + 5]), (float)(curY += _argStack[i + 6]), //dxe,dye (float)(curX += _argStack[i + 7]), (float)(curY) //dxf,+0 ); // count -= 8; i += 8; } } _currentX = curX; _currentY = curY; } break; case 4: case 5: { //|- dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf? hvcurveto (31) |- //If there is a multiple of four arguments, the curve starts //horizontal and ends vertical. //Note that the curves alternate between start horizontal, end vertical, and start vertical, and //end horizontal.The last curve(the odd argument case) need not //end horizontal/ vertical. double curX = _currentX; double curY = _currentY; int count = _currentIndex; if (count == 5) { //last one _glyphTranslator.Curve4( (float)(curX += _argStack[i + 0]), (float)(curY), //dx1 (float)(curX += _argStack[i + 1]), (float)(curY += _argStack[i + 2]), //dx2,dy2 (float)(curX += _argStack[i + 4]), (float)(curY += _argStack[i + 3]) //dx3,dy3 ); count -= 5; i += 5; } else { _glyphTranslator.Curve4( (float)(curX += _argStack[i + 0]), (float)(curY), //dx1 (float)(curX += _argStack[i + 1]), (float)(curY += _argStack[i + 2]), //dx2,dy2 (float)(curX), (float)(curY += _argStack[i + 3]) //dy3 ); count -= 4; i += 4; } while (count > 0) { _glyphTranslator.Curve4( (float)(curX), (float)(curY += _argStack[i + 0]), //0,dya (float)(curX += _argStack[i + 1]), (float)(curY += _argStack[i + 2]), //dxb,dyb (float)(curX += _argStack[i + 3]), (float)(curY) //dxc, +0 ); if (count == 9) { //last cycle _glyphTranslator.Curve4( (float)(curX += _argStack[i + 4]), (float)(curY), //dxd,0 (float)(curX += _argStack[i + 5]), (float)(curY += _argStack[i + 6]), //dxe,dye (float)(curX += _argStack[i + 8]), (float)(curY += _argStack[i + 7]) //dxf,dyf ); i += 9; count -= 9; } else { _glyphTranslator.Curve4( (float)(curX += _argStack[i + 4]), (float)(curY), //dxd,0 (float)(curX += _argStack[i + 5]), (float)(curY += _argStack[i + 6]), //dxe,dye (float)(curX), (float)(curY += _argStack[i + 7]) //0,dyf ); // i += 8; count -= 8; } } _currentX = curX; _currentY = curY; } break; } _currentIndex = 0; //clear stack } public void R_CurveLine() { #if DEBUG #endif //|- { dxa dya dxb dyb dxc dyc} +dxd dyd rcurveline(24) |- //is equivalent to one rrcurveto for each set of six arguments //dxa...dyc, followed by exactly one rlineto using //the dxd, dyd arguments. //The number of curves is determined from the count //on the argument stack. double curX = _currentX; double curY = _currentY; int i = 0; int count = _currentIndex; while (count > 0) { _glyphTranslator.Curve4( (float)(curX += _argStack[i + 0]), (float)(curY += _argStack[i + 1]), //dxa,dya (float)(curX += _argStack[i + 2]), (float)(curY += _argStack[i + 3]), //dxb,dyb (float)(curX += _argStack[i + 4]), (float)(curY += _argStack[i + 5]) //dxc,dyc ); // i += 6; count -= 6; if (count == 2) { //last one _glyphTranslator.LineTo((float)(curX += _argStack[i]), (float)(curY += _argStack[i + 1])); break;//exit while } } _currentX = curX; _currentY = curY; _currentIndex = 0; //clear stack } public void R_LineCurve() { #if DEBUG #endif //|- { dxa dya} +dxb dyb dxc dyc dxd dyd rlinecurve(25) |- //is equivalent to one rlineto for each pair of arguments beyond //the six arguments dxb...dyd needed for the one //rrcurveto command.The number of lines is determined from the count of //items on the argument stack. double curX = _currentX; double curY = _currentY; int i = 0; int count = _currentIndex; while (count > 0) { _glyphTranslator.LineTo( (float)(curX += _argStack[i + 0]), (float)(curY += _argStack[i + 1])); // i += 2; count -= 2; if (count == 6) { //last one _glyphTranslator.Curve4( (float)(curX += _argStack[i + 0]), (float)(curY += _argStack[i + 1]), //dxa,dya (float)(curX += _argStack[i + 2]), (float)(curY += _argStack[i + 3]), //dxb,dyb (float)(curX += _argStack[i + 4]), (float)(curY += _argStack[i + 5]) //dxc,dyc ); break;//exit while } } _currentX = curX; _currentY = curY; _currentIndex = 0; //clear stack } public void VH_CurveTo() { #if DEBUG #endif //|- dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf? vhcurveto (30) |- //|- {dya dxb dyb dxc dxd dxe dye dyf}+ dxf? vhcurveto (30) |- //appends one or more Bézier curves to the current point, where //the first tangent is vertical and the second tangent is horizontal. //This command is the complement of //hvcurveto; //see the description of hvcurveto for more information. int i = 0; int remainder = 0; switch (remainder = (_currentIndex % 8)) { default: throw new OpenFontNotSupportedException(); case 0: case 1: { //|- {dya dxb dyb dxc dxd dxe dye dyf}+ dxf? vhcurveto (30) |- double curX = _currentX; double curY = _currentY; int ncount = _currentIndex; while (ncount > 0) { _glyphTranslator.Curve4( (float)(curX), (float)(curY += _argStack[i + 0]), //+0,dya (float)(curX += _argStack[i + 1]), (float)(curY += _argStack[i + 2]), //dxb,dyb (float)(curX += _argStack[i + 3]), (float)(curY) //dxc,+0 ); if (ncount == 9) { //last cycle _glyphTranslator.Curve4( (float)(curX += _argStack[i + 4]), (float)(curY), //dxd,+0 (float)(curX += _argStack[i + 5]), (float)(curY += _argStack[i + 6]), //dxe,dye (float)(curX += _argStack[i + 8]), (float)(curY += _argStack[i + 7]) //+dxf,dyf ); // i += 9; ncount -= 9; } else { _glyphTranslator.Curve4( (float)(curX += _argStack[i + 4]), (float)(curY), //dxd,+0 (float)(curX += _argStack[i + 5]), (float)(curY += _argStack[i + 6]), //dxe,dye (float)(curX), (float)(curY += _argStack[i + 7]) //+0,dyf ); // i += 8; ncount -= 8; } } _currentX = curX; _currentY = curY; } break; case 4: case 5: { //|- dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf? vhcurveto (30) |- double curX = _currentX; double curY = _currentY; int ncount = _currentIndex; if (ncount == 5) { //only 1 _glyphTranslator.Curve4( (float)(curX), (float)(curY += _argStack[i + 0]), //dy1 (float)(curX += _argStack[i + 1]), (float)(curY += _argStack[i + 2]), //dx2,dy2 (float)(curX += _argStack[i + 3]), (float)(curY += _argStack[i + 4]) //dx3,dyf ); i += 5; ncount -= 5; } else { _glyphTranslator.Curve4( (float)(curX), (float)(curY += _argStack[i + 0]), //dy1 (float)(curX += _argStack[i + 1]), (float)(curY += _argStack[i + 2]), //dx2,dy2 (float)(curX += _argStack[i + 3]), (float)(curY) //dx3 ); i += 4; ncount -= 4; } while (ncount > 0) { //line to _glyphTranslator.Curve4( (float)(curX += _argStack[i + 0]), (float)(curY), //dxa, (float)(curX += _argStack[i + 1]), (float)(curY += _argStack[i + 2]), //dxb,dyb (float)(curX), (float)(curY += _argStack[i + 3]) //+0, dyc ); if (ncount == 9) { //last cycle _glyphTranslator.Curve4( (float)(curX), (float)(curY += _argStack[i + 4]), //+0,dyd (float)(curX += _argStack[i + 5]), (float)(curY += _argStack[i + 6]), //dxe,dye (float)(curX += _argStack[i + 7]), (float)(curY += _argStack[i + 8]) //dxf,dyf ); i += 9; ncount -= 9; } else { _glyphTranslator.Curve4( (float)(curX), (float)(curY += _argStack[i + 4]), //+0,dyd (float)(curX += _argStack[i + 5]), (float)(curY += _argStack[i + 6]), //dxe,dye (float)(curX += _argStack[i + 7]), (float)(curY) //dxf,0 ); i += 8; ncount -= 8; } } _currentX = curX; _currentY = curY; } break; } _currentIndex = 0; //clear stack } public void VV_CurveTo() { // |- dx1? {dya dxb dyb dyc}+ vvcurveto (26) |- //appends one or more curves to the current point. //If the argument count is a multiple of four, the curve starts and ends vertical. //If the argument count is odd, the first curve does not begin with a vertical tangent. int i = 0; int count = _currentIndex; double curX = _currentX; double curY = _currentY; if ((count % 2) != 0) { //odd number _glyphTranslator.Curve4( (float)(curX += _argStack[0]), (float)(curY += _argStack[1]), //dx1?,+dya (float)(curX += _argStack[2]), (float)(curY += _argStack[3]), //dxb,dyb (float)(curX), (float)(curY += _argStack[4]) //+0,+dyc ); i += 5; count -= 5; } while (count > 0) { _glyphTranslator.Curve4( (float)(curX), (float)(curY += _argStack[i + 0]), //+0,dya (float)(curX += _argStack[i + 1]), (float)(curY += _argStack[i + 2]), //dxb,dyb (float)(curX), (float)(curY += _argStack[i + 3]) //+0,dyc ); // i += 4; count -= 4; } _currentX = curX; _currentY = curY; _currentIndex = 0; //clear stack } public void EndChar() { _currentIndex = 0; } public void Flex() { //|- dx1 dy1 dx2 dy2 dx3 dy3 dx4 dy4 dx5 dy5 dx6 dy6 fd flex (12 35) |- //causes two Bézier curves, as described by the arguments(as //shown in Figure 2 below), to be rendered as a straight line when //the flex depth is less than fd / 100 device pixels, and as curved lines // when the flex depth is greater than or equal to fd/ 100 device pixels _currentIndex = 0; //clear stack } public void H_Flex() { //|- dx1 dx2 dy2 dx3 dx4 dx5 dx6 hflex (12 34) |- //causes the two curves described by the arguments //dx1...dx6 to be rendered as a straight line when //the flex depth is less than 0.5(that is, fd is 50) device pixels, //and as curved lines when the flex depth is greater than or equal to 0.5 device pixels. //hflex is used when the following are all true: //a) the starting and ending points, first and last control points //have the same y value. //b) the joining point and the neighbor control points have //the same y value. //c) the flex depth is 50. _currentIndex = 0; //clear stack } public void H_Flex1() { //|- dx1 dy1 dx2 dy2 dx3 dx4 dx5 dy5 dx6 hflex1 (12 36) |- //causes the two curves described by the arguments to be //rendered as a straight line when the flex depth is less than 0.5 //device pixels, and as curved lines when the flex depth is greater //than or equal to 0.5 device pixels. //hflex1 is used if the conditions for hflex //are not met but all of the following are true: //a) the starting and ending points have the same y value, //b) the joining point and the neighbor control points have //the same y value. //c) the flex depth is 50. _currentIndex = 0; //clear stack } public void Flex1() { //|- dx1 dy1 dx2 dy2 dx3 dy3 dx4 dy4 dx5 dy5 d6 flex1 (12 37) | //causes the two curves described by the arguments to be //rendered as a straight line when the flex depth is less than 0.5 //device pixels, and as curved lines when the flex depth is greater //than or equal to 0.5 device pixels. //The d6 argument will be either a dx or dy value, depending on //the curve(see Figure 3). To determine the correct value, //compute the distance from the starting point(x, y), the first //point of the first curve, to the last flex control point(dx5, dy5) //by summing all the arguments except d6; call this(dx, dy).If //abs(dx) > abs(dy), then the last point’s x-value is given by d6, and //its y - value is equal to y. // Otherwise, the last point’s x-value is equal to x and its y-value is given by d6. _currentIndex = 0; //clear stack } //------------------------------------------------------------------- //4.3 Hint Operators public void H_Stem() { //|- y dy {dya dyb}* hstem (1) |- #if DEBUG if ((_currentIndex % 2) != 0) { throw new OpenFontNotSupportedException(); } #endif //hintCount += _currentIndex / 2; _currentIndex = 0; //clear stack } public void V_Stem() { //|- x dx {dxa dxb}* vstem (3) |- #if DEBUG if ((_currentIndex % 2) != 0) { throw new OpenFontNotSupportedException(); } #endif //hintCount += _currentIndex / 2; _currentIndex = 0; //clear stack } public void V_StemHM() { //|- x dx {dxa dxb}* vstemhm (23) |- #if DEBUG if ((_currentIndex % 2) != 0) { throw new OpenFontNotSupportedException(); } #endif //hintCount += _currentIndex / 2; _currentIndex = 0; //clear stack } public void H_StemHM() { //|- y dy {dya dyb}* hstemhm (18) |- #if DEBUG if ((_currentIndex % 2) != 0) { throw new OpenFontNotSupportedException(); } #endif //hintCount += _currentIndex / 2; //has the same meaning as //hstem (1), //except that it must be used //in place of hstem if the charstring contains one or more //hintmask operators. _currentIndex = 0; //clear stack } //---------------------------------------- //hintmask | -hintmask(19 + mask) | - //The mask data bytes are defined as follows: //• The number of data bytes is exactly the number needed, one //bit per hint, to reference the number of stem hints declared //at the beginning of the charstring program. //• Each bit of the mask, starting with the most-significant bit of //the first byte, represents the corresponding hint zone in the //order in which the hints were declared at the beginning of //the charstring. //• For each bit in the mask, a value of ‘1’ specifies that the //corresponding hint shall be active. A bit value of ‘0’ specifies //that the hint shall be inactive. //• Unused bits in the mask, if any, must be zero. public void HintMask1(int hintMaskValue) { //specifies which hints are active and which are not active. If any //hints overlap, hintmask must be used to establish a nonoverlapping //subset of hints. //hintmask may occur any number of //times in a charstring. Path operators occurring after a hintmask //are influenced by the new hint set, but the current point is not //moved. If stem hint zones overlap and are not properly //managed by use of the hintmask operator, the results are //undefined. //|- hintmask (19 + mask) |- _currentIndex = 0; //clear stack } public void HintMask2(int hintMaskValue) { _currentIndex = 0; //clear stack } public void HintMask3(int hintMaskValue) { _currentIndex = 0; //clear stack } public void HintMask4(int hintMaskValue) { _currentIndex = 0; //clear stack } public void HintMaskBits(int bitCount) { //calculate bytes need by //bytes need = (bitCount+7)/8 #if DEBUG if (_currentIndex != (bitCount + 7) / 8) { } #endif _currentIndex = 0; //clear stack } //---------------------------------------- //|- cntrmask(20 + mask) |- //specifies the counter spaces to be controlled, and their relative //priority.The mask bits in the bytes, following the operator, //reference the stem hint declarations; the most significant bit of //the first byte refers to the first stem hint declared, through to //the last hint declaration.The counters to be controlled are //those that are delimited by the referenced stem hints.Bits set to //1 in the first cntrmask command have top priority; subsequent //cntrmask commands specify lower priority counters(see Figure //1 and the accompanying example). public void CounterSpaceMask1(int cntMaskValue) { _currentIndex = 0;//clear stack } public void CounterSpaceMask2(int cntMaskValue) { _currentIndex = 0;//clear stack } public void CounterSpaceMask3(int cntMaskValue) { _currentIndex = 0;//clear stack } public void CounterSpaceMask4(int cntMaskValue) { _currentIndex = 0;//clear stack } public void CounterSpaceMaskBits(int bitCount) { //calculate bytes need by //bytes need = (bitCount+7)/8 #if DEBUG if (_currentIndex != (bitCount + 7) / 8) { } #endif _currentIndex = 0;//clear stack } //---------------------------------------- //4.4: Arithmetic Operators //case Type2Operator2.abs: // case Type2Operator2.add: // case Type2Operator2.sub: // case Type2Operator2.div: // case Type2Operator2.neg: // case Type2Operator2.random: // case Type2Operator2.mul: // case Type2Operator2.sqrt: // case Type2Operator2.drop: // case Type2Operator2.exch: // case Type2Operator2.index: // case Type2Operator2.roll: // case Type2Operator2.dup: public void Op_Abs() { Debug.WriteLine("NOT_IMPLEMENT:" + nameof(Op_Abs)); } public void Op_Add() { Debug.WriteLine("NOT_IMPLEMENT:" + nameof(Op_Add)); } public void Op_Sub() { Debug.WriteLine("NOT_IMPLEMENT:" + nameof(Op_Sub)); } public void Op_Div() { Debug.WriteLine("NOT_IMPLEMENT:" + nameof(Op_Div)); } public void Op_Neg() { Debug.WriteLine("NOT_IMPLEMENT:" + nameof(Op_Neg)); } public void Op_Random() { Debug.WriteLine("NOT_IMPLEMENT:" + nameof(Op_Random)); } public void Op_Mul() { Debug.WriteLine("NOT_IMPLEMENT:" + nameof(Op_Mul)); } public void Op_Sqrt() { Debug.WriteLine("NOT_IMPLEMENT:" + nameof(Op_Sqrt)); } public void Op_Drop() { Debug.WriteLine("NOT_IMPLEMENT:" + nameof(Op_Drop)); } public void Op_Exch() { Debug.WriteLine("NOT_IMPLEMENT:" + nameof(Op_Exch)); } public void Op_Index() { Debug.WriteLine("NOT_IMPLEMENT:" + nameof(Op_Index)); } public void Op_Roll() { Debug.WriteLine("NOT_IMPLEMENT:" + nameof(Op_Roll)); } public void Op_Dup() { Debug.WriteLine("NOT_IMPLEMENT:" + nameof(Op_Dup)); } //------------------------- //4.5: Storage Operators //The storage operators utilize a transient array and provide //facilities for storing and retrieving transient array data. //The transient array provides non-persistent storage for //intermediate values. //There is no provision to initialize this array, //except explicitly using the put operator, //and values stored in the //array do not persist beyond the scope of rendering an individual //character. public void Put() { Debug.WriteLine("NOT_IMPLEMENT:" + nameof(Put)); } public void Get() { Debug.WriteLine("NOT_IMPLEMENT:" + nameof(Get)); } //------------------------- //4.6: Conditional public void Op_And() { Debug.WriteLine("NOT_IMPLEMENT:" + nameof(Op_And)); } public void Op_Or() { Debug.WriteLine("NOT_IMPLEMENT:" + nameof(Op_Or)); } public void Op_Not() { Debug.WriteLine("NOT_IMPLEMENT:" + nameof(Op_Not)); } public void Op_Eq() { Debug.WriteLine("NOT_IMPLEMENT:" + nameof(Op_Eq)); } public void Op_IfElse() { Debug.WriteLine("NOT_IMPLEMENT:" + nameof(Op_IfElse)); } public double Pop() { #if DEBUG if (_currentIndex < 1) { } #endif return (double)_argStack[--_currentIndex];//*** use prefix } public void Ret() { #if DEBUG if (_currentIndex > 0) { } #endif _currentIndex = 0; } #if DEBUG public void dbugClearEvalStack() { _currentIndex = 0; } #endif } }