4

I've a piece of code that is taking an equation and converting it to a .png image for display. If the equation contains a comma(,) - upon conversion to the .png, the , is lost if the image byte string has been base64 encoded. If not, everything is fine and the comma displays. An example - Non base64 - equation 1,2 displays as image of 1,2 Base64 - equation 1,2 display as image of 12

The equation is derived from the querystring. eg -

/.../.../GetEquationImage.aspx?eq=1%2C3&fontsize=13&fontfamily=RobotoDraft%2C%20Helvetica%20Neue%2C%20Helvetica%2C%20Arial%2C%20sans-serif&fontcolor=%23000000&base64=true

eq is the querystring value we are using. In the above case it is '1%2C3' The url is encoded using javascript encodeURIComponent When I grab the above value from the query string, it's evaluated as 1,3 so I don't think there are any issues there.

One fix I've found is to do the following:

sEquation = sEquation.Replace(",", ",");

However this causes a lot of knock-on issues which would be very problematic to fix, so looking for another alternative.

Here's the code that I'm currently running:

protected void Page_Load(object sender, EventArgs e)
{
    Response.Clear();

    string sEquation = Request["eq"];
    if (String.IsNullOrEmpty(sEquation))
        return;

    string sBase64 = Request["base64"];
    bool bBase64Output = sBase64 == "true";

    string sFontSize = Request["fontsize"];
    string sFontFamily = Request["fontfamily"];
    string sFontColor = Request["fontcolor"];

    if (string.IsNullOrEmpty(sFontSize))
    {
        sFontSize = "12";
    }
    sFontSize = Regex.Match(sFontSize, @"\d*").Value;
    float fFontSize;
    if (String.IsNullOrEmpty(sFontSize) || !float.TryParse(sFontSize, out fFontSize))
        fFontSize = 12f;

    //adjust font size for personal zoom level
    int iPerId = GetCurrentPersonId(null);
    double dZoom = iPerId < 0 ? 1 : itPersonCache.GetEquationZoomLevel(Context, DatabaseAccess.GetDatabase(Context), iPerId, null);
    fFontSize *= (float)dZoom;

    Debug.Write("Generating equation image for \"" + sEquation + "\"...");

    //create the equation object and obtain equation bitmap...
    var oEquation = new Equation(sEquation.Replace("&code", "&#"), sFontFamily, fFontSize, sFontColor); //legacy replacement for old equations
    MemoryStream oStream;
    using (Bitmap oEquationBitmap = oEquation.Draw()) {
        //...encode the bitmap as png image...
        ImageCodecInfo oEncoder = GetEncoder(ImageFormat.Png);
        var oParameters = new EncoderParameters();
        var oQualityParam = new EncoderParameter(Encoder.Quality, 100L);
        oParameters.Param[0] = oQualityParam;
        oStream = new MemoryStream();
        oEquationBitmap.Save(oStream, oEncoder, oParameters);
    }
    //...then send the image as the response
    if (bBase64Output) {
        byte[] ImageBytes = oStream.ToArray();
        Response.ContentType = "text/plain";
        Response.Write(Convert.ToBase64String(ImageBytes));
        //piggyback baseline and height for equation alignment
        Response.Write(string.Format("baseline={0}height={1}zoom={2}", (int)Math.Round(oEquation.BaselineOffset, 0, MidpointRounding.AwayFromZero), oEquation.ImageHeight, dZoom));
        Debug.WriteLine(string.Format("sent via base64 text/plain ({0} bytes)", ImageBytes.Length));
    }
    else {
        Response.ContentType = "image/png";
        Response.BufferOutput = true;
        oStream.WriteTo(Response.OutputStream);
        Debug.WriteLine("sent via image/png");
    }
}

Any suggestions on another way to get round this issue? Its fairly fundamental that the equation is displayed as is, so losing the comma is a big no-no. The base64 encoding is also a requirement. And the fix I mentioned above sEquation = sEquation.Replace(",", "&comma;"); is also a no-no given the knock on impact. Any help really appreciated.

/// <summary>
    /// returns a bitmap with rendered formula
    /// </summary>
    /// <returns></returns>
    override public Bitmap Draw()
    {
        if (oBaseSymbol == null || oBaseSymbol.fW <= 0 || oBaseSymbol.fH <= 0)
            return oImage;

        int iMargin = (int)Math.Round((BASE_MARGIN_LEFT + BASE_MARGIN_RIGHT) * oBaseSymbol.fFontSize);
        int iWidth = (int)Math.Round(oBaseSymbol.fW) + iMargin;
        oImage = new Bitmap(iWidth, (int)Math.Round(oBaseSymbol.fH));
        oGraphics = Graphics.FromImage(oImage);
        oGraphics.Clear(Color.Transparent);

        // Set the text antialiasing and bitmap interoplation modes (so that bitmaps that are added
        // to the equation bitmap are smoothed).
        oGraphics.TextContrast = 0;
        oGraphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
        oGraphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

        foreach (Symbol oSymbol in lsoSymbols) {
            if (oSymbol.sType == "Symbol" || oSymbol.sType == "Expression") {
                float fX = oSymbol.fGX + oSymbol.oSymbolData.fPaddingLeft * oSymbol.fFontSize;
                float fY = oSymbol.fGY;
                // For Cambria Math symbols fY must be reduced.
                if (oSymbol.oSymbolData.bUseMathsFont)
                    fY -= MATHS_FONT_VERTICAL_SPACE_FACTOR / 2 * oSymbol.fFontSize;

                oGraphics.DrawString(oSymbol.oSymbolData.sOut, oSymbol.oFont, oSymbol.oBrush, new PointF(fX, fY));


                if (bDrawDebugLines) {
                    oGraphics.DrawRectangle(Pens.Blue, fX, fY, fX + oSymbol.fW, fY + oSymbol.fH);
                    oGraphics.DrawLine(Pens.Red, fX, fY + oSymbol.fBaselineOffset, fX + oSymbol.fW, fY + oSymbol.fBaselineOffset);
                }


            }
            else if (oSymbol.sType == "MText") {
                float fX = oSymbol.fGX;
                float fY = oSymbol.fGY;
                oGraphics.DrawString(oSymbol.sStr, oSymbol.oFont, oSymbol.oBrush, new PointF(fX, fY));
            }

            foreach (Rect oRect in oSymbol.lsoRectangles) {
                if (!String.IsNullOrEmpty(oRect.sImageToUse)) {
                    if (oRect.sImageToUse == "Dot")
                        oGraphics.DrawString(".", oSymbol.oFont, oSymbol.oBrush, new PointF(oSymbol.fGX + oRect.fX, oSymbol.fGY + oRect.fY));
                    else if (oRect.sImageToUse == "DDot")
                        oGraphics.DrawString("..", oSymbol.oFont, oSymbol.oBrush, new PointF(oSymbol.fGX + oRect.fX, oSymbol.fGY + oRect.fY));
                    else if (oRect.sImageToUse == "Tilde")
                        oGraphics.DrawString("~", oSymbol.oFont, oSymbol.oBrush, new PointF(oSymbol.fGX + oRect.fX, oSymbol.fGY + oRect.fY));
                    else if (oRect.sImageToUse == "Hat")
                        oGraphics.DrawString(HttpUtility.HtmlDecode("&#94;"), oSymbol.oFont, oSymbol.oBrush, new PointF(oSymbol.fGX + oRect.fX, oSymbol.fGY + oRect.fY));
                    else
                        lock (oDrawImageLock)
                        {
                            oGraphics.DrawImage(GetImage(oRect.sImageToUse), oSymbol.fGX + oRect.fX, oSymbol.fGY + oRect.fY, oRect.fW, oRect.fH);
                        }
                }
                else
                    oGraphics.FillRectangle(Brushes.Black, (int)Math.Round(oSymbol.fGX + oRect.fX), (int)Math.Round(oSymbol.fGY + oRect.fY), (int)Math.Round(oRect.fW), (int)Math.Round(oRect.fH));
            }
        }


        if (bDrawDebugLines) {
            oGraphics.DrawLine(Pens.Green, 0, oBaseSymbol.fBaselineOffset, oBaseSymbol.fGX + oBaseSymbol.fW, oBaseSymbol.fBaselineOffset);
            oGraphics.DrawRectangle(Pens.Blue, 0, 0, oBaseSymbol.fW, oBaseSymbol.fH - 1);
        }

        return oImage;
    }
Terry Delahunt
  • 616
  • 4
  • 21
  • I suspect the bug may lie in the `Equation.Draw()` method, but you haven't provided the code for that... – Ian Kemp Mar 15 '16 at 10:52
  • I've since resolved the issue somewhat, but have included the Equation.Draw() method to see if anyone might spot an issue there. I cannot myself. My fix is more a hack imho, but it does a job. – Terry Delahunt Mar 15 '16 at 11:21
  • 1
    Are you parsing that `1,3` somewhere to a number valuetype between the construction of the Equation class and the call to Draw on that instance? – rene Mar 27 '16 at 11:57
  • @rene no mate, we don't do that. We solved it by amending the querystring value before it arrives into the code where Equation.Draw() is called. I'd post the answer but its really specific to our app and probably no use to anyone here, its a hack really but it got us over the hump with it. Appreciate the response. – Terry Delahunt Mar 29 '16 at 11:22
  • You probably just need to use URL safe base 64 encoding. – Maarten Bodewes Apr 18 '16 at 22:42

0 Answers0