Edit me

Using Font in PostScript with GDI+ Graphics Path

Converting WPF Geometry To GDI+ Graphics Path

  • Since, there is no way to convert WPF PathGeometry to GDI+ Graphics Path directly, I used SVG Path converstion.
    • Fitst, WPF PathGeometry is converted to SVG Path

      PathGeometry lPathGeom = lGeom.GetOutlinedPathGeometry();
      string lsGeom = lPathGeom.ToString();

    • Then, convert SVG to GDI+ Path

      lsGeom = lsGeom.Replace(‘E’, ‘e’);
      Svg2.Pathing.SvgPathSegmentList llSvgPathSegments = Svg2.SvgPathBuilder.Parse(lsGeom);
      foreach (Svg2.Pathing.SvgPathSegment lSvgPath in llSvgPathSegments)
      {
      lSvgPath.AddToPath(lGPath);
      }
      lPData = lGPath.PathData;

public static System.Drawing.Drawing2D.PathData GeometryToGraphicsPath(System.Windows.Media.Geometry lGeom)
{
    System.Drawing.Drawing2D.PathData lPData = null;
    try
    {
        if (lGeom == null)
            return lPData;

        if (lGeom.IsEmpty())  // for SPACE
        {
            lPData = new System.Drawing.Drawing2D.PathData();
            if (lPData.Points == null)
                lPData.Points = new System.Drawing.PointF[1] { new System.Drawing.PointF() };
            if (lPData.Types == null)
                lPData.Types = new byte[1] { 0 };
            return lPData;
        }
        using (System.Drawing.Drawing2D.GraphicsPath lGPath = new System.Drawing.Drawing2D.GraphicsPath())
        {
            PathGeometry lPathGeom = lGeom.GetOutlinedPathGeometry();
            string lsGeom = lPathGeom.ToString();
            lsGeom = lsGeom.Replace('E', 'e');
            Svg2.Pathing.SvgPathSegmentList llSvgPathSegments = Svg2.SvgPathBuilder.Parse(lsGeom);
            foreach (Svg2.Pathing.SvgPathSegment lSvgPath in llSvgPathSegments)
            {
                lSvgPath.AddToPath(lGPath);
            }
            lPData = lGPath.PathData;
            if (lPData.Points == null)
                lPData.Points = new System.Drawing.PointF[1] { new System.Drawing.PointF() };
            if (lPData.Types == null)
                lPData.Types = new byte[1] { 0 };
        }

        for (int IX = 0;IX < lPData.Points.Length;IX++)
        {
            lPData.Points[IX] = new System.Drawing.PointF((float)UCNV.GetPointFromDIU(lPData.Points[IX].X), (float)UCNV.GetPointFromDIU(lPData.Points[IX].Y));
        }                
    }
    catch (Exception lExe)
    {
        lPData = null;
        ORIONDEBUG.LOG(LogInfo.EnumLogLevel.ERROR, "WpfFontCache::ToGraphicsPath()", lExe);
    }

    return lPData;
}

GDI+ Graphics Path To PostScript Path

  • GDI+ Path Data contains below path commands

    Path value == 0x00 : Move To
    Path value == 0x01 : Line To
    Path value == 0x03 : Point of Cubic Bezier Spline
    Path value == 0x80 : Close Path

internal void WriteUPath(string lsPathName, PathData lPathD)
{
    UPathHeader(lsPathName);

    PointF[] lptfaPathPoint = lPathD.Points;
    List<PointF> llptfCurveTo = new List<PointF>();
    byte[] lbyaPathType = lPathD.Types;
    bool lbIsLastCommandCLOSEPATH = true;
    for (int idx = 0; idx < lptfaPathPoint.Length; idx++)
    {
        PointF lPP = lptfaPathPoint[idx];
        lPP.Y = -lPP.Y;
        byte lPT = lbyaPathType[idx];

        if (llptfCurveTo.Count == 3)
        {
            PS_curveto(llptfCurveTo);
            llptfCurveTo.Clear();
        }
        if (lPT >= 0x80)
        {
            if (llptfCurveTo.Count == 0)
            {
                PS_lineto(lPP.X, lPP.Y);
            }
            else
            {
                llptfCurveTo.Add(lPP);
                PS_curveto(llptfCurveTo);
            }
            llptfCurveTo.Clear();
        }
        if (lPT == 0x00)    // Indicates that the point is the start of a figure.
        {
            if (!lbIsLastCommandCLOSEPATH)
                PS_closepath();
            PS_moveto(lPP.X, lPP.Y);
            lbIsLastCommandCLOSEPATH = false;
        }
        else if (lPT == 0x01)   // Indicates that the point is one of the two endpoints of a line.
        {
            PS_lineto(lPP.X, lPP.Y);
            lbIsLastCommandCLOSEPATH = false;
        }
        else if (lPT == 0x03)   // Indicates that the point is an endpoint or control point of a cubic Bezier spline.
        {
            llptfCurveTo.Add(lPP);
            lbIsLastCommandCLOSEPATH = false;
            //PS_LineTo(lFStream, lPP.X, lPP.Y);
        }
        else if (lPT == 0x07)   // Masks all bits except for the three low-order bits, which indicate the point type.
        {
            lbIsLastCommandCLOSEPATH = false;
        }
        else if (lPT == 0x20)   // Specifies that the point is a marker.
        {
            lbIsLastCommandCLOSEPATH = false;
        }
        else if (lPT == 0x80)   // Specifies that the point is the last point in a closed subpath (figure).
        {
            PS_closepath();
            lbIsLastCommandCLOSEPATH = true;
        }
        else if (lPT == 0x83)
        {
            PS_closepath();
            lbIsLastCommandCLOSEPATH = true;
        }
        else if (lPT == 0xa3)
        {
            PS_closepath();
            lbIsLastCommandCLOSEPATH = true;
        }
        else
        {
        }
    }
    if (!lbIsLastCommandCLOSEPATH)
        PS_closepath();
    UPathTail();
}

private void UPathHeader(string lstrProcedureName)
{
    WriteLF();
    WriteUnicodeToASCII_LF("/" + lstrProcedureName);
    WriteUnicodeToASCII_LF("{");
}
private void UPathTail()
{
    WriteUnicodeToASCII_LF("} bind def");
}
internal void PS_curveto(List<PointF> llptfCurveTo)
{
    string lstrPostScript = string.Empty;
    for (int idx = 0; idx < llptfCurveTo.Count; idx++)
    {
        lstrPostScript += llptfCurveTo[idx].X.ToString("0.###") + " " + llptfCurveTo[idx].Y.ToString("0.###") + " ";
    }
    lstrPostScript += csPScurveto;
    WriteUnicodeToASCII_LF(lstrPostScript);
}
internal void PS_moveto(float lfX, float lfY)
{
    WriteUnicodeToASCII_LF(String.Format("{0:0.###} {1:0.###} {2}", lfX, lfY, csPSmoveto));
}
internal void PS_lineto(float lfX, float lfY)
{
    WriteUnicodeToASCII_LF(String.Format("{0:0.###} {1:0.###} {2}", lfX, lfY, csPSlineto));
}
internal void PS_closepath()
{
    WriteUnicodeToASCII_LF(csPSclosepath);
}

Drawing GDI+ Font Path in PostScript

  • Set position and rotation

    if (this.fRotation == 0F)
    {
    lPD.cPSBody.WriteUnicodeToASCII(String.Format(“{0:0.###} {1:0.###} {2} “,
    lptfAPos.X + lptfAdjPos.X, lptfAPos.Y - lptfAdjPos.Y, OrionPostScript.csPStranslate));
    }
    else
    {
    lPD.cPSBody.PS_translate(lptfAPos.X, lptfAPos.Y);
    lPD.cPSBody.PS_rotate(-this.fRotation);
    lPD.cPSBody.PS_translate(lptfAdjPos.X, -lptfAdjPos.Y);
    }

  • Font definitions of character ‘A’(F_0) and ‘가’(F_1) in PostScript header

    /F_0
    {
    3.257 -4.542 m
    3.221 -4.767 3.182 -4.925 3.14 -5.016 c
    2.002 -8.121 l
    4.536 -8.121 l
    3.389 -5.016 l
    3.346 -4.902 3.311 -4.744 3.281 -4.542 c
    3.257 -4.542 l
    h
    2.881 -3.698 m
    3.682 -3.698 l
    6.46 -10.885 l
    5.586 -10.885 l
    4.805 -8.844 l
    1.724 -8.844 l
    0.996 -10.885 l
    0.117 -10.885 l
    2.881 -3.698 l
    2.881 -3.698 l
    h
    } bind def

    /F_1
    {
    1.24 -3.185 m
    5.64 -3.185 l
    5.568 -5.857 4.155 -7.975 1.401 -9.537 c
    0.84 -8.947 l
    1.826 -8.54 2.71 -7.88 3.491 -6.969 c
    4.272 -6.058 4.728 -5.013 4.858 -3.834 c
    1.24 -3.834 l
    1.24 -3.185 l
    h
    7.231 -2.345 m
    7.959 -2.345 l
    7.959 -6.227 l
    9.648 -6.227 l
    9.648 -6.876 l
    7.959 -6.876 l
    7.959 -11.764 l
    7.231 -11.764 l
    7.231 -2.345 l
    7.231 -2.345 l
    h
    } bind def

  • Drawing character ‘A’, ‘가’ in PostScript body at Page#1

    %%Page: 1 1
    %%PageBoundingBox: 0 0 595 842
    %%BeginPageSetup
    /olddevice currentpagedevice def
    <<<br/> /PageSize [595 842]
    >> setpagedevice
    0 rotate 0 0 translate
    %%EndPageSetup
    q
    56.693 799.37 T 0 0 0 r
    q 0 0 T 1.9 1.9 s F_0 f Q : CHAR ‘A’ = F_0
    Q
    q
    56.693 771.024 T 0 0 0 r
    q 0 0 T 1.9 1.9 s F_1 f Q : CHAR ‘가’ = F_1
    Q
    b

lPD.cPSBody.PS_gsave();

try
{

    if (this.fRotation == 0F)
    {
        lPD.cPSBody.WriteUnicodeToASCII(String.Format("{0:0.###} {1:0.###} {2} ", 
                    lptfAPos.X + lptfAdjPos.X, lptfAPos.Y - lptfAdjPos.Y, OrionPostScript.csPStranslate));
    }
    else
    {
        lPD.cPSBody.PS_translate(lptfAPos.X, lptfAPos.Y);
        lPD.cPSBody.PS_rotate(-this.fRotation);
        lPD.cPSBody.PS_translate(lptfAdjPos.X, -lptfAdjPos.Y);
    }


    float lfFontSize = this.GetVariableFontSize();
    float lfFontScaleW = lfFontSize / PostScriptFontData._DEFFONTSIZE;
    float lfFontScaleH = lfFontSize / PostScriptFontData._DEFFONTSIZE;
    //if (this.cfFontHWRatio != 100F)
    lfFontScaleW *= lfScaleWidth;// / 100F;

    lPD.cPSBody.PS_setrgbcolor(lForeColor);
    if (this.bFontStyleBold)
    {
        lPD.cPSBody.PS_setlinewidth(PostScriptFontData._DEFFONTSIZE * 0.030F);
    }

    foreach (OrionTextBoxInfo.LineChars lLine in llLNChars)
    {
        PointF lptfRPos = new PointF(OrionConfigInfo.UCNV.GetPointFromPixel(lLine.PTF.X, lPD.fWidthDPI),
                                        -OrionConfigInfo.UCNV.GetPointFromPixel(lLine.PTF.Y, lPD.fHeightDPI));

        if (this.bFontStyleItalic || this.cbFontOutline)
        {
            foreach (OrionTextBoxInfo.SizeFChar lSZFCH in lLine.LSZFCH)
            {
                SizeF lszfCharGap = new SizeF(OrionConfigInfo.UCNV.GetPointFromPixel((float)lSZFCH.GapHorz, lPD.fWidthDPI),
                                            OrionConfigInfo.UCNV.GetPointFromPixel((float)lSZFCH.GapVert, lPD.fHeightDPI));
                if (lSZFCH.CH != '\u200B')
                    lPD.cPSBody.PS_WriteChar(lPD, this, lptfRPos,
                                            lfFontScaleW, lfFontScaleH,
                                            lfFontSize, lSZFCH);

                lptfRPos.X += lszfCharGap.Width;
            }
        }
        else
        {
            StringBuilder lSB = new StringBuilder();
            lSB.AppendFormat(OrionPostScript.csPSgsave + " {0:0.##} {1:0.##} {2} ", lptfRPos.X, lptfRPos.Y, OrionPostScript.csPStranslate);
            if (lfFontScaleW != 1.0F || lfFontScaleH != 1.0F)
                lSB.AppendFormat("{0:0.##} {1:0.##} {2} ", lfFontScaleW, lfFontScaleH, OrionPostScript.csPSscale);

            bool lbIsFirstChar = true;
            float lfGapHPrevChar = 0F;
            float lfXPosTransform = 0F;
            //
            List<OrionConfigInfo.OrionColor> llColors = new List<OrionConfigInfo.OrionColor>();
            int liColorIX = 0;
            if (cColorList.cbUseColorList)
                llColors = cColorList.GetColors(lLine.LSZFCH.Count);
            //
            if (this.cbFontFlipHorz || this.cbFontFlipVert)
            {
                foreach (OrionTextBoxInfo.SizeFChar lSZFCH in lLine.LSZFCH)
                {
                    if (lSZFCH.CH == '\u200B')
                    {
                        lfGapHPrevChar = OrionConfigInfo.UCNV.GetPointFromPixel((float)lSZFCH.GapHorz, lPD.fWidthDPI);
                        lptfRPos.X += lfGapHPrevChar;
                        continue;
                    }
                    if (cColorList.cbUseColorList)
                    {
                        NewColor lNCR = new NewColor(llColors[liColorIX]);
                        liColorIX++;
                        lPD.cPSBody.PS_setrgbcolor(lSB, lNCR.RGB);
                    }
                    //
                    lSB.AppendFormat("{0}\n", OrionPostScript.csPSgsave);
                    //
                    if (lSZFCH.cGlyph != null && lSZFCH.cGlyph.isSubstituted && lPD.fontSubstitutionPositionAdjust)
                    {
                        double adjustSubstitutedFontPositionY = lSZFCH.cGlyph.baselineDifference * 0.7;
                        lSB.AppendFormat("[1 0 0 1 {0:0.##} {1:0.##}] {2}\n",
                            0,
                            -adjustSubstitutedFontPositionY,
                            OrionPostScript.csPSconcat);
                    }
                    //
                    lfXPosTransform += lfGapHPrevChar;
                    lSB.AppendFormat("[1 0 0 1 {0:0.##} 0] {1}\n", lfXPosTransform / lfFontScaleW, OrionPostScript.csPSconcat);
                    if (this.cbFontFlipVert)
                    {
                        lSB.AppendFormat("[1 0 0 -1 {0:0.##} {1:0.##}] {2}\n",
                            0,
                            -OrionConfigInfo.UCNV.GetPointFromPixel((float)lSZFCH.CharHeight, lPD.fHeightDPI) / lfFontScaleH,
                            OrionPostScript.csPSconcat);
                    }
                    if (this.cbFontFlipHorz)
                    {
                        lSB.AppendFormat("[-1 0 0 1 {0:0.##} {1:0.##}] {2}\n",
                            OrionConfigInfo.UCNV.GetPointFromPixel((float)lSZFCH.CharWidth, lPD.fWidthDPI) / lfFontScaleW * 1.35F,
                            0,
                            OrionPostScript.csPSconcat);
                    }
                    lSB.Append(lSZFCH.FontCache + "\n");

                    if (this.bFontStyleBold)
                    {
                        lSB.Append(OrionPostScript.csPSgsave + " " + OrionPostScript.csPSfill + " " + OrionPostScript.csPSgrestore +
                            " [] 0 " + OrionPostScript.csPSsetdash + " " + OrionPostScript.csPSstroke + " ");
                    }
                    else
                    {
                        lSB.Append(OrionPostScript.csPSfill + " ");
                    }

                    lSB.AppendFormat("{0}\n", OrionPostScript.csPSgrestore);


                    lfGapHPrevChar = OrionConfigInfo.UCNV.GetPointFromPixel((float)lSZFCH.GapHorz, lPD.fWidthDPI);
                    lptfRPos.X += lfGapHPrevChar;

                }
            }
            else
            {
                foreach (OrionTextBoxInfo.SizeFChar lSZFCH in lLine.LSZFCH)
                {
                    if (lSZFCH.CH == '\u200B')
                    {
                        lfGapHPrevChar = OrionConfigInfo.UCNV.GetPointFromPixel((float)lSZFCH.GapHorz, lPD.fWidthDPI);
                        lptfRPos.X += lfGapHPrevChar;
                        continue;
                    }

                    if (lSZFCH.cGlyph != null && lSZFCH.cGlyph.isSubstituted && lPD.fontSubstitutionPositionAdjust)
                    {
                        double adjustSubstitutedFontPositionY = lSZFCH.cGlyph.baselineDifference * 0.7;
                        lSB.AppendFormat("[1 0 0 1 {0:0.##} {1:0.##}] {2}\n",
                            0,
                            -adjustSubstitutedFontPositionY,
                            OrionPostScript.csPSconcat);

                        if (lbIsFirstChar)
                        {
                            lSB.Append(lSZFCH.FontCache + " ");
                            lbIsFirstChar = false;
                        }
                        else
                        {
                            lSB.AppendFormat("{0:0.##} X {1} ", lfGapHPrevChar / lfFontScaleW, lSZFCH.FontCache);
                        }
                        lSB.AppendFormat("[1 0 0 1 {0:0.##} {1:0.##}] {2}\n",
                            0,
                            adjustSubstitutedFontPositionY,
                            OrionPostScript.csPSconcat);
                    }
                    else
                    {
                        if (lbIsFirstChar)
                        {
                            lSB.Append(lSZFCH.FontCache + " ");
                            lbIsFirstChar = false;
                        }
                        else
                        {
                            lSB.AppendFormat("{0:0.##} X {1} ", lfGapHPrevChar / lfFontScaleW, lSZFCH.FontCache);
                        }
                    }
                    //


                    lfGapHPrevChar = OrionConfigInfo.UCNV.GetPointFromPixel((float)lSZFCH.GapHorz, lPD.fWidthDPI);
                    lptfRPos.X += lfGapHPrevChar;
                    if (cColorList.cbUseColorList)
                    {
                        NewColor lNCR = new NewColor(llColors[liColorIX]);
                        liColorIX++;
                        lPD.cPSBody.PS_setrgbcolor(lSB, lNCR.RGB);
                        if (this.bFontStyleBold)
                        {
                            lSB.Append(OrionPostScript.csPSgsave + " " + OrionPostScript.csPSfill + " " + OrionPostScript.csPSgrestore +
                                " [] 0 " + OrionPostScript.csPSsetdash + " " + OrionPostScript.csPSstroke + " ");
                        }
                        else
                        {
                            lSB.Append(OrionPostScript.csPSfill + " ");
                        }

                    }
                }
                if (!cColorList.cbUseColorList)
                {
                    if (this.bFontStyleBold)
                    {
                        lSB.Append(OrionPostScript.csPSgsave + " " + OrionPostScript.csPSfill + " " + OrionPostScript.csPSgrestore +
                            " [] 0 " + OrionPostScript.csPSsetdash + " " + OrionPostScript.csPSstroke + " ");
                    }
                    else
                    {
                        lSB.Append(OrionPostScript.csPSfill + " ");
                    }
                }
            }
            lSB.AppendLine(OrionPostScript.csPSgrestore);
            lPD.cPSBody.WriteUnicodeToASCII(lSB.ToString());
        }

        if (this.bFontStyleStrikeout || this.bFontStyleUnderline)
            this.DrawSThru_ULine_PS(lPD, lPD.cPSBody, lLine);

    }
}
catch (Exception exe)
{
    ORIONDEBUG.LOG(LogInfo.EnumLogLevel.ERROR, "Error in OD_ColumnData::DrawData_PS()", exe);
}
finally
{
    lPD.cPSBody.PS_grestore();
    lStrFmt.Dispose();
}
Tags: