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(",", ",");
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("^"), 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;
}