Thursday, 19 September 2013

MeasureString length is different from CSS Width

MeasureString length is different from CSS Width

I have a program that let's users create a canvas and drag/drop/resize
text onto that canvas using jquery ui. I submit the CSS & DOM details via
JSON to the server to draw these out and return a pdf. I also scale up the
size of the canvas and all related elements to draw them at higher
resolution using the GDI in .NET.
This all works great, except around line-height. This is defined in the
CSS as the same as the font-size in px. So, when I draw text, I split the
text string and draw word by assembling a line and then moving to the next
one by the font size. I do this word by word and measure the string
against the text's parent div's width. 99% this works, but occasionally
there is a difference when the word is wrapped in my calculation and what
is in the browser (screenshot below).
I've added a 1% buffer to the width in .NET to catch these, but there is
always a slight discrepancy that seems to change depending on the font.
Is there a way I can preserve the same wrap in browser in .NET? Is there a
better way to draw out text than what I am doing? Can I change CSS to act
more like .NET in the word wrap detection? This seems like a minor
problem, but clients expect parity between what they create and what gets
generated, so I'm going nuts here trying to find a good solution. I'm open
to anything, thanks!
Example Pic: The word 'pick' wraps onto the next line when I draw it in
.NET. The right side is the editor in the browser, while the rendered
result is on the left.

.NET code:
else if (c.Item is textObj)
{
textObj t = (textObj)c.Item;
if (string.IsNullOrWhiteSpace(t.text) ||
t.text.Length == 0)
continue;
//get color
//SolidBrush textColor = new
SolidBrush(ColorTranslator.FromHtml(t.color));
SolidBrush textColor = new
SolidBrush(Color.FromArgb(alphaBrush,
ColorTranslator.FromHtml(t.color)));
//get font
//get optional font style
//FontFamily ff = new FontFamily(t.font);
//ff.IsStyleAvailable(FontStyle.Regular);
FontStyle fs = FontStyle.Regular;
if (t.italic)
{
fs = fs | FontStyle.Italic;
}
if (t.bold)
{
fs = fs | FontStyle.Bold;
}
if (t.underline)
{
fs = fs | FontStyle.Underline;
}
InstalledFontCollection fontsCollection = new
InstalledFontCollection();
FontFamily[] fontFamilies = fontsCollection.Families;
List<string> fonts = new List<string>();
FontFamily ff = new FontFamily(t.font);
ff = fontsCollection.Families.Where(n => n.Name ==
t.font).FirstOrDefault();
Font f = new Font(t.font, t.font_size, fs,
GraphicsUnit.Pixel, 0);
//get alignment
StringFormat frmt;
switch (t.align)
{
case "center":
frmt = new StringFormat { LineAlignment =
StringAlignment.Center, Alignment =
StringAlignment.Center, FormatFlags =
StringFormatFlags.NoClip };
break;
case "left":
frmt = new StringFormat { LineAlignment =
StringAlignment.Near, Alignment =
StringAlignment.Near, FormatFlags =
StringFormatFlags.NoClip };
break;
case "right":
frmt = new StringFormat { LineAlignment =
StringAlignment.Far, Alignment =
StringAlignment.Far, FormatFlags =
StringFormatFlags.NoClip };
break;
default:
frmt = new StringFormat { LineAlignment =
StringAlignment.Near, Alignment =
StringAlignment.Near, FormatFlags =
StringFormatFlags.NoClip };
break;
}
t.text = this.CleanString(t.text);
//split string
List<string> words = t.text.Split(new char[]{'
',}).ToList();
words = HandleHyphens(words);
string currentLine = string.Empty;
int currentWord = 0;
int currentY = t.coord.y;
SizeF stringSize = new SizeF();
bool IsHyphenated = false;
foreach (string w in words)
{
string testLine;
if (w.Length > 1 && LeftRightMid.Right(w, 1)
== "-")
{
if (IsHyphenated)
{
//handles multiple hyphendated words
testLine = currentLine + w;
}
else
{
testLine = currentLine + " " + w;
}
IsHyphenated = true;
}
else if (IsHyphenated)
{
testLine = currentLine + w;
IsHyphenated = false;
}
else
{
if (currentWord > 0 &&
words[currentWord-1] == "-")
{
testLine = currentLine + w;
}
else
{
testLine = currentWord == 0 ? w :
currentLine + " " + w;
}
//testLine = currentLine + " " + w;
IsHyphenated = false;
}
stringSize = g.MeasureString(testLine, f);
if (stringSize.Width < (t.size.width * 1.01))
//if (stringSize.Width < (t.size.width))
{
currentLine = testLine;
}
else
{
Rectangle lineRect = new
Rectangle(t.coord.x, currentY,
(int)(t.size.width*1.01),
(int)stringSize.Height);
g.DrawString(currentLine.TrimStart(' '),
f, textColor, lineRect, frmt);
currentY += t.font_size;
currentLine = w;
}
currentWord++;
}
//write last line
g.DrawString(currentLine.TrimStart(' '), f,
textColor, new Rectangle(t.coord.x, currentY,
(int)(t.size.width * 1.063),
(int)stringSize.Height), frmt);
HTML of text object:
<div id="text1379600411940" class="canvas_object text ui-draggable
ui-resizable selected" style="width: 491px; height: 322px; position:
absolute; left: 240.9px; top: 413px; z-index: 1007; opacity: 1;">
<div class="ui-resizable-handle ui-resizable-ne" style="z-index: 90;
display: block;"></div>
<div class="ui-resizable-handle ui-resizable-se ui-icon
ui-icon-gripsmall-diagonal-se" style="z-index: 90; display: block;"></div>
<div class="ui-resizable-handle ui-resizable-sw" style="z-index: 90;
display: block;"></div>
<div class="ui-resizable-handle ui-resizable-nw" style="z-index: 90;
display: block;"></div>
<div class="canvas_obj_text align_left" style="font-size: 29px;
line-height: 29px; font-family: Cabin;">One whiff and you know this is
serious stuff. The aromas of baking brioche, coconut, candied citrus and
leather pick up roasted coffee and grilled nuts on the palate, permeating
the senses. Profound depth and complexity, offering a unique Champagne
experience. Drink now through 2006. 40,000 cases made. –BS</div>
</div>

No comments:

Post a Comment