function truncateSingleLine(textLine, options) {
    var containerWidth = options.containerWidth;
    var font = options.font;
    var contentWidth = options.contentWidth;

    if (!containerWidth) {
        return '';
    }

    var lineWidth = getWidth(textLine, font);

    if (lineWidth <= containerWidth) {
        return textLine;
    }

    for (var j = 0; ; j++) {
        if (lineWidth <= contentWidth || j >= options.maxIterations) {
            textLine += options.ellipsis;
            break;
        }

        var subLength = j === 0
            ? estimateLength(textLine, contentWidth, options.ascCharWidth, options.cnCharWidth)
            : lineWidth > 0
            ? Math.floor(textLine.length * contentWidth / lineWidth)
            : 0;

        textLine = textLine.substr(0, subLength);
        lineWidth = getWidth(textLine, font);
    }

    if (textLine === '') {
        textLine = options.placeholder;
    }

    return textLine;
}
@public
@param {string} text
@param {string} font
@param {Object} [truncate]
@return {Object} block: {lineHeight, lines, height, outerHeight, canCacheByTextString}
 Notice: for performance, do not calculate outerWidth util needed.
 `canCacheByTextString` means the result `lines` is only determined by the input `text`.
 Thus we can simply comparing the `input` text to determin whether the result changed,
 without travel the result `lines`.