Calculate text width in pixels server-side

Problem

I'm trying to use the following code on my server to estimate the length of some text fields and potentially trim them before sending them by email...

String.prototype.visualLength = function()
{
    var element = document.createElement("span");
    element.css({
        "visibility": "hidden",
        "white-space": "nowrap",
        "font-weight": "normal",
        "font-size": "18px",
        "line-height": "40px",
        "font-family": "Helvetica, Arial, sans-serif",
        "text-decoration": "none"
    });
    document.body.appendChild(element);
    element.innerHTML = this;
    return element.offsetWidth;
};

String.prototype.trimToPx = function(length)
{
    var tmp = this;
    var trimmed = this;
    if (tmp.visualLength() > length)
    {
        trimmed += "...";
        while (trimmed.visualLength() > length)
        {
            tmp = tmp.substring(0, tmp.length-1);
            trimmed = tmp + "...";
        }
    };
    return trimmed;
};

Obviously I'm getting an error because "document" is not defined server-side. I added the Meteor packages htmljs and domutils hoping they might solve this, but in vain. And I can't add the Node.js package jsdom because apparently it won't work in deployed Meteor apps.

Any idea how to achieve what I'm trying to do?

Problem courtesy of: dimfisch

Solution

You cannot truly rely what will happen on client-side. Even Ubuntu and Windows shows the fonts different because of different hinting, antialiasing and they may have an effect on the displayed sizes.

If you modify your span's css as following, when it has a text larger than the desired space, no matter what, the remaining will be displayed as ...

max-width: 10%;
border: 1px #000 solid; // JUST TO SEE the effect clearly
width: 10%;
overflow: hidden;
height: 20px;
text-overflow: ellipsis;
display: block;
white-space: nowrap;

** Please note that you need a proper height value, other wise the text will go to bottom and what you want will not be achieved.

Try on http://jsfiddle.net/sG6H2/

Solution courtesy of: Mustafa

Discussion

I don't think this is possible. Server-side code running in node.js does not run in the context of a web browser's rendering environment and so can't access the browser's DOM and available fonts and display configuration. Even if you were to successfully run your code and compute a number, that computation has no relationship to my device when I receive your email. The fonts, the display size and resolution (could be a desktop, could be a mobile device), my own configuration (for example I set a minimum font size in Firefox on my desktop), etc. Instead try defining your size in em units instead of pixels.

Discussion courtesy of: dsh

This recipe can be found in it's original form on Stack Over Flow.