Рендеринг открытого текста в виде пробела с поддержкой HTML - без <pre> - PullRequest
8 голосов
/ 15 февраля 2011

Учитывая любой произвольный текстовый файл, полный печатных символов, как это можно преобразовать в HTML, который будет отображаться точно так же (с учетом следующих требований)?

  • Не опирается ни на какие правила HTML, кроме пробелов по умолчанию.
    • Нет <pre> тег
    • Нет CSS white-space правила
  • <p> метки в порядке, но не обязательны (<br /> с и / или <div> с в порядке)
  • Пробелы сохраняются точно.

    С учетом следующих строк ввода (игнорировать ошибочную подсветку автоматического синтаксиса):

    Line one
        Line two, indented    four spaces
    

    Браузер должен отображать вывод точно так же, сохраняя отступ второй строки и разрыв между «отступом» и «пробелами». Конечно, я на самом деле не ищу моноширинный вывод, и шрифт ортогонален алгоритму / разметке.

    Учитывая две строки как полный входной файл, пример правильного вывода будет:

    Line one<br />&nbsp;&nbsp;&nbsp;&nbsp;Line two, 
    indented&nbsp;&nbsp;&nbsp; four spaces
    
  • Желательно мягкое завертывание в браузере. То есть результирующий HTML не должен заставлять пользователя прокручиваться, даже если входные строки шире, чем его область просмотра (при условии, что отдельные слова все еще сужаются, чем указанная область просмотра).

Я ищу полностью определенный алгоритм. Бонусные баллы за реализацию в python или javascript .

(Пожалуйста, не просто отвечайте, что я должен использовать теги <pre> или правило CSS white-space, поскольку мои требования делают эти параметры несостоятельными. Просьба также не публиковать непроверенные и / или наивные предложения, такие как как «заменить все пробелы на &nbsp;». В конце концов, я уверен, что решение технически возможно - это интересная проблема, не правда ли?)

Ответы [ 4 ]

14 голосов
/ 15 февраля 2011

Решение, позволяющее браузеру переносить длинные строки , заключается в замене каждой последовательности из двух пробелов пробелом и без пробела.

Браузер будет корректно отображать все пробелы (обычные и без перерывов), но по-прежнему переносить длинные строки (из-за обычных пробелов).

Javascript:

text = html_escape(text); // dummy function
text = text.replace(/\t/g, '    ')
           .replace(/  /g, '&nbsp; ')
           .replace(/  /g, ' &nbsp;') // second pass
                                      // handles odd number of spaces, where we 
                                      // end up with "&nbsp;" + " " + " "
           .replace(/\r\n|\n|\r/g, '<br />');
10 голосов
/ 19 февраля 2011

Используйте пробел нулевой ширины (&#8203;), чтобы сохранить пробелы и разрешить перенос текста.Основная идея заключается в соединении каждого пробела или последовательности пробелов с пробелом нулевой ширины.Затем замените каждый пробел неразрывным пробелом.Вы также захотите закодировать html и добавить разрывы строк.

Если вас не интересуют символы Юникода, это тривиально.Вы можете просто использовать string.replace():

function textToHTML(text)
{
    return ((text || "") + "")  // make sure it is a string;
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/\t/g, "    ")
        .replace(/ /g, "&#8203;&nbsp;&#8203;")
        .replace(/\r\n|\r|\n/g, "<br />");
}

Если разрешено переносить пустое пространство, соедините каждое пространство с пробелом нулевой ширины, как указано выше.В противном случае, чтобы сохранить пробелы вместе, соедините каждую последовательность пробелов с пробелом нулевой ширины:

    .replace(/ /g, "&nbsp;")
    .replace(/((&nbsp;)+)/g, "&#8203;$1&#8203;")

Чтобы кодировать символы Юникода, это немного сложнее.Вам нужно перебрать строку:

var charEncodings = {
    "\t": "&nbsp;&nbsp;&nbsp;&nbsp;",
    " ": "&nbsp;",
    "&": "&amp;",
    "<": "&lt;",
    ">": "&gt;",
    "\n": "<br />",
    "\r": "<br />"
};
var space = /[\t ]/;
var noWidthSpace = "&#8203;";
function textToHTML(text)
{
    text = (text || "") + "";  // make sure it is a string;
    text = text.replace(/\r\n/g, "\n");  // avoid adding two <br /> tags
    var html = "";
    var lastChar = "";
    for (var i in text)
    {
        var char = text[i];
        var charCode = text.charCodeAt(i);
        if (space.test(char) && !space.test(lastChar) && space.test(text[i + 1] || ""))
        {
            html += noWidthSpace;
        }
        html += char in charEncodings ? charEncodings[char] :
        charCode > 127 ? "&#" + charCode + ";" : char;
        lastChar = char;
    }
    return html;
}  

Теперь просто комментарий.Без использования моноширинных шрифтов вы потеряете некоторое форматирование.Рассмотрим, как эти строки текста с моноширинным шрифтом образуют столбцы:

ten       seven spaces
eleven    four spaces

Без моноширинного шрифта вы потеряете столбцы:

десять семь пробелов
одиннадцать четырех пробелов

Похоже, что алгоритм исправления будет очень сложным.

2 голосов
/ 15 февраля 2011

Хотя это не совсем соответствует всем вашим требованиям & mdash; во-первых, он не обрабатывает вкладки, я использовал следующий гем, который добавляет метод wordWrap() в Javascript String s, пару раз, чтобы сделать что-то похожее на то, что вы описываете & mdash; так что это может быть хорошей отправной точкой, чтобы придумать что-то, что также делает дополнительные вещи, которые вы хотите.

//+ Jonas Raoni Soares Silva
//@ http://jsfromhell.com/string/wordwrap [rev. #2]

// String.wordWrap(maxLength: Integer,
//                 [breakWith: String = "\n"],
//                 [cutType: Integer = 0]): String
//
//   Returns an string with the extra characters/words "broken".
//
//     maxLength  maximum amount of characters per line
//     breakWith  string that will be added whenever one is needed to
//                break the line
//     cutType    0 = words longer than "maxLength" will not be broken
//                1 = words will be broken when needed
//                2 = any word that trespasses the limit will be broken

String.prototype.wordWrap = function(m, b, c){
    var i, j, l, s, r;
    if(m < 1)
        return this;
    for(i = -1, l = (r = this.split("\n")).length; ++i < l; r[i] += s)
        for(s = r[i], r[i] = ""; s.length > m; r[i] += s.slice(0, j) + ((s = s.slice(j)).length ? b : ""))
            j = c == 2 || (j = s.slice(0, m + 1).match(/\S*(\s)?$/))[1] ? m : j.input.length - j[0].length
            || c == 1 && m || j.input.length + (j = s.slice(m).match(/^\S*/)).input.length;
    return r.join("\n");
};

Я также хотел бы прокомментировать, что мне кажется, что в общем случае вы хотите использовать моноширинный шрифт, если используются вкладки, потому что ширина слов будет зависеть от используемого пропорционального шрифта (что приводит к результатам использования табуляции очень сильно зависит от шрифта).

Обновление : Вот немного более читаемая версия, предоставленная онлайн javascript beautifier :

String.prototype.wordWrap = function(m, b, c) {
    var i, j, l, s, r;
    if (m < 1)
        return this;
    for (i = -1, l = (r = this.split("\n")).length; ++i < l; r[i] += s)
        for (s = r[i], r[i] = ""; s.length > m; r[i] += s.slice(0, j) + ((s =
                s.slice(j)).length ? b : ""))
            j = c == 2 || (j = s.slice(0, m + 1).match(/\S*(\s)?$/))[1] ? m :
            j.input.length - j[0].length || c == 1 && m || j.input.length +
            (j = s.slice(m).match(/^\S*/)).input.length;
    return r.join("\n");
};
0 голосов
/ 24 января 2014

Это очень просто, если вы используете библиотеку jQuery в своем проекте.

Всего одна строка, добавьте asHTml расширение к String Class и:

var plain='&lt;a&gt; i am text plain &lt;/a&gt;'
plain.asHtml();
/* '<a> i am text plain </a>' */

ДЕМО: http://jsfiddle.net/abdennour/B6vGG/3/

Примечание. Вам не потребуется доступ к DoM. Просто используйте шаблон проектирования JQuery $('<tagName />')

...