поиск "разрывов строк" в текстовой области, которая является переносящим текст арабским текстом - PullRequest
14 голосов
/ 18 января 2011

У меня есть строка текста, которая отображается в текстовой области (справа налево).Пользователь может динамически изменять размер текстовой области (для этого я использую jquery), и текст будет по мере необходимости переноситься.

Когда пользователь нажимает кнопку «Отправить», я возьму этот текст и создам изображение с помощью PHP, НО, прежде чем отправить сообщениеЯ хотел бы знать, где происходят "разрывы строк" или, скорее, "переносы слов".

Везде, где я до сих пор смотрел, только показано, как обрабатывать разрывы строк на стороне php.Я хочу прояснить, что нет никаких разрывов.У меня есть одна длинная строка, которая будет обернута в слова различными способами в зависимости от ширины текстовой области, установленной пользователем.

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

Если кто-нибудь знает способ доступа, где встречаются переносы слов (либо в текстовой области, либо в div, если нужно -быть), я бы очень хотел знать.

Мое единственное другое решение - на самом деле хранить (в моей БД) ширину каждого отдельного символа (несколько утомительно, поскольку в 600 различных шрифтах более 200 символов,всего ... какое-то огромное количество).

Мои надежды не велики, но я подумал, что спросить.

Спасибо

i.Джамал

Ответы [ 7 ]

14 голосов
/ 18 января 2011

Что ж, вместо нахождения разрывов строк (что практически невозможно) вы можете принудительно переместить их в текстовую область, используя эту функцию:

function ApplyLineBreaks(strTextAreaId) {
    var oTextarea = document.getElementById(strTextAreaId);
    if (oTextarea.wrap) {
        oTextarea.setAttribute("wrap", "off");
    }
    else {
        oTextarea.setAttribute("wrap", "off");
        var newArea = oTextarea.cloneNode(true);
        newArea.value = oTextarea.value;
        oTextarea.parentNode.replaceChild(newArea, oTextarea);
        oTextarea = newArea;
    }

    var strRawValue = oTextarea.value;
    oTextarea.value = "";
    var nEmptyWidth = oTextarea.scrollWidth;
    var nLastWrappingIndex = -1;
    for (var i = 0; i < strRawValue.length; i++) {
        var curChar = strRawValue.charAt(i);
        if (curChar == ' ' || curChar == '-' || curChar == '+')
            nLastWrappingIndex = i;
        oTextarea.value += curChar;
        if (oTextarea.scrollWidth > nEmptyWidth) {
            var buffer = "";
            if (nLastWrappingIndex >= 0) {
                for (var j = nLastWrappingIndex + 1; j < i; j++)
                    buffer += strRawValue.charAt(j);
                nLastWrappingIndex = -1;
            }
            buffer += curChar;
            oTextarea.value = oTextarea.value.substr(0, oTextarea.value.length - buffer.length);
            oTextarea.value += "\n" + buffer;
        }
    }
    oTextarea.setAttribute("wrap", "");
}

Эта функция получает идентификатор текстовой области, и всякий раз, когда происходит перенос слов, она вставляет новый разрыв строки в текстовой области.Запустите функцию в форме отправки, и вы получите текст с правильными переносами строк в коде на стороне сервера.

Успешно протестировано для IE, Chrome и Firefox, не стесняйтесь увидеть здесь: http://jsfiddle.net/yahavbr/pH79a/1/ (Предварительный просмотр покажет новые строки)

13 голосов
/ 02 ноября 2013

Вот функционально эквивалентная реализация решения Shadow Wizard, которая намного быстрее, потому что она использует бинарный поиск вместо линейного поиска для определения длины каждой строки:

function ApplyLineBreaks(strTextAreaId) {
    var oTextarea = document.getElementById(strTextAreaId);
    if (oTextarea.wrap) {
        oTextarea.setAttribute("wrap", "off");
    }
    else {
        oTextarea.setAttribute("wrap", "off");
        var newArea = oTextarea.cloneNode(true);
        newArea.value = oTextarea.value;
        oTextarea.parentNode.replaceChild(newArea, oTextarea);
        oTextarea = newArea;
    }

    var strRawValue = oTextarea.value;
    oTextarea.value = "";
    var nEmptyWidth = oTextarea.scrollWidth;

    function testBreak(strTest) {
        oTextarea.value = strTest;
        return oTextarea.scrollWidth > nEmptyWidth;
    }
    function findNextBreakLength(strSource, nLeft, nRight) {
        var nCurrent;
        if(typeof(nLeft) == 'undefined') {
            nLeft = 0;
            nRight = -1;
            nCurrent = 64;
        }
        else {
            if (nRight == -1)
                nCurrent = nLeft * 2;
            else if (nRight - nLeft <= 1)
                return Math.max(2, nRight);
            else
                nCurrent = nLeft + (nRight - nLeft) / 2;
        }
        var strTest = strSource.substr(0, nCurrent);
        var bLonger = testBreak(strTest);
        if(bLonger)
            nRight = nCurrent;
        else
        {
            if(nCurrent >= strSource.length)
                return null;
            nLeft = nCurrent;
        }
        return findNextBreakLength(strSource, nLeft, nRight);
    }

    var i = 0, j;
    var strNewValue = "";
    while (i < strRawValue.length) {
        var breakOffset = findNextBreakLength(strRawValue.substr(i));
        if (breakOffset === null) {
            strNewValue += strRawValue.substr(i);
            break;
        }
        var nLineLength = breakOffset - 1;
        for (j = nLineLength - 1; j >= 0; j--) {
            var curChar = strRawValue.charAt(i + j);
            if (curChar == ' ' || curChar == '-' || curChar == '+') {
                nLineLength = j + 1;
                break;
            }
        }
        strNewValue += strRawValue.substr(i, nLineLength) + "\n";
        i += nLineLength;
    }
    oTextarea.value = strNewValue;
    oTextarea.setAttribute("wrap", "");
}

Обновленная скрипка .

3 голосов
/ 15 ноября 2011

Я думаю, что самый простой способ сделать это - установить перенос слов вашей текстовой области в HTML-коде на 'hard', например:

<textarea id='yourTextArea' wrap='hard'></textarea>

Это означает, что везде, где ваша текстовая область нарушаетстрока для вас также будет вставлять символ \ разрыв строки в строке при отправке.Если вы обнаружите, что это проходит через вашу строку, то вам будет легко определить, где изначально был разрыв.Или вы также можете превратить этих персонажей вHTML-теги с функцией nl2br () PHP.

1 голос
/ 09 января 2016

Вот мой пример для вычисления фактического количества строк (после переноса строк) для каждой строки текста в текстовой области. Обратите внимание, что ширина текста текстовой области немного уменьшается, когда начинает появляться полоса прокрутки . Это может привести к дальнейшему переносу предыдущего содержимого, так что ранее вычисленная высота строки не будет правильной. Следовательно, текстовое поле должно иметь CSS-стиль overflow-y, установленный на «прокрутка», чтобы принудительно постоянно отображать полосу прокрутки .

function get_row_wraps(txtArea){                        
    if(wrap=="off"){                                    
        var out=[];                                     
        for(var i=txtArea.split_lines.length; i>=0; --i)
            out[i]=1;                                   
        return out;                                     
    }                                                   

    var its=txtArea.value.split("\n");                        
    var newArea = txtArea.cloneNode(true);              
    newArea.hidden=true;                                
    newArea.style.visibility = "hidden";                
    txtArea.parentNode.appendChild(newArea);            

    // get single row height                            
    newArea.style.height="auto";                        
    newArea.style.overflowY="scroll";                   
    newArea.value="1\n2\n3";                            
    var unit_height=newArea.scrollHeight;               
    newArea.value="1\n2\n3\n4";                         
    var unit_height=newArea.scrollHeight-unit_height;   
    newArea.style.height=Math.round(unit_height*1.5)+"px"; // so that the scrollbar does not vanish
    newArea.value="";                                   

    // obtain row height for every line of text         
    function comp_Nrow(scroll_height){                  
        return Math.floor(scroll_height/unit_height);   
    }                                                   
    function calc_rows(txt){                            
        newArea.value+=txt;                             
        return comp_Nrow(newArea.scrollHeight);         
    }                                                   
    var out=[];                                         
    for(var i=0; i<its.length; i++)                     
        out.push(calc_rows(i==0?its[i]:("\n"+its[i]))); 
    txtArea.parentNode.removeChild(newArea);            
    for(var i=out.length-1; i>0; i--)                   
        out[i]-=out[i-1];                               
//  alert(out);                                         
    return out;                                         
}                                                       

Вышеприведенная функция возвращает фактическое количество строк в каждой строке текста (разделенных "\ n") в текстовой области. Вычисления точны, по крайней мере, для Chrome и Firefox.

1 голос
/ 27 января 2011

По какой-то причине меня никогда не предупреждали об обновлении этого поста ... а прошлой ночью у меня была эта ЯРКАЯ идея о том, как определить, где были разрывы строк ... Я перестроил бы строку и проверил ширинукаждый раз, и это работало, поэтому я пришел сюда, чтобы поделиться им ... и обнаружил, что я на 1 неделю отстал

В любом случае 2 важные вещи

  1. Используемый вами код используетта же самая блестящая идея, которая у меня была (молодец), НО, когда я тестирую ее, она правильно разбивает первую строку, а затем добавляет разрыв строки после каждого символа (проверено по ссылке jsfiddle.net)

  2. Я добавил свой код, который использует jquery и использует ширину диапазона, чтобы определить, когда нужно разбить. Сначала я попытался использовать ширину div, но div.width () возвращает ширину по умолчанию, а не ширинуcontent.

Я осведомлен, что МОЖЕТ НЕ РАБОТАТЬ НА ВСЕХ БРАУЗЕРОВ поэтому я прошу любезно спросить, если кто-нибудь знает способ сделать это надежнымэто, пожалуйста, поделитесь.

FiВо-первых, стили необходимы для синхронизации шрифтов (всех атрибутов) между textarea и div, установки размера и (для IE) удаления любых полос прокрутки, которые появляются автоматически.


    .inputArea {
      width:200px; 
      height:100px; 
      font-family:Arial; 
      font-size:12px; 
      overflow: auto; 
      border: 1px solid #cccccc;
      padding:0;
      margin:0;
    }
    .divArea {
      font-family:Arial; 
      font-size:12px;
    }
  

Далее я включаю jquery имои пользовательские функции:

  
  <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js
"></script>
  <script type="text/javascript">
  $(document).ready(function() {
     $("#breakUp").click(function () {
       showLineBreaks(); 
       addLineBreaks(); 
     });

     function showLineBreaks() {
       content = $("#textEntered").val();
       //replace line breaks in content with "|" to allow for replacement below
       content = content.replace("\r\n", "<br>");
       content = content.replace("\r", "<br>");
       content = content.replace("\n", "<br>");
       $("#unedited").html(content);
     }

     function addLineBreaks() {
       content = $("#textEntered").val();
       //replace line breaks in content with "|" to allow for replacement below
       content = content.replace("\r\n", "|");
       content = content.replace("\r", "|");
       content = content.replace("\n", "|");
       tempContent = "";
       $("#edited").html("");
       for (var i = 0; i ");
         } else { 
           tempContent = $("#edited").html();
           $("#edited").html(tempContent + content.charAt(i));
           if ($("#edited").width() > 200) {
             $("#edited").html(tempContent + "<br>" + content.charAt(i));
           }
         }
       }
     }
  });
  <script>

И, наконец, моя html тестовая страница


  Enter text into the textarea below (Set to 200 px width, 100 px height)<br>
  <textarea id="textEntered" class="inputArea"></textarea>
  <br><br>
  The div below will display that text WITHOUT wrapping, BUT replacing all existing line breaks with <br><br>
  <div id="unedited"></div>
  <br>
  The following div will display that text with line breaks ADDED to fit the wrapping<br>
  <div class="divArea"><span id="edited"></span></div>  
  <br>
  <button id="breakUp">Click Here to Convert</button>
0 голосов
/ 10 апреля 2019

Я не хочу копать старые сообщения, но это то, где я вышел после поиска в течение некоторого времени. Решение @Shadow Wizard у меня не сработало, потому что оно оставляло место за каждым предложением, поэтому я решил внести некоторые коррективы. Вместо того, чтобы делать это за персонажа, я делаю это за слово. Это значительно упрощает проверки, а также в 10 раз быстрее, с полной Lorem Ipsum (~ 600 мс против 6 полных секунд на скрипке, 120 мс без скрипки).

Я создал (задокументированную) скрипку. Я посмотрю, смогу ли я еще немного ее оптимизировать.

function applyLineBreaks = function(strTextAreaId) {

    // Get txtarea
    var oTextarea = document.getElementById(strTextAreaId);

    // Disable textarea wrap
    oTextarea.setAttribute("wrap", "off");

    // Split the characters into an array
    var aWords = oTextarea.value.split(' ');

    // Empty the textarea
    oTextarea.value = "";

    // Get textarea scrollwidth
    var nEmptyWidth = oTextarea.scrollWidth;

    // Start looping over the words
    for(var i = 0; i < aWords.length; i++)
    {
        if(i > 1000)
        {
            break;
        }
        var curWord = aWords[i] + ' ';

        // Add character to textarea
        oTextarea.value += curWord;

        // console.log(oTextarea.scrollWidth, nEmptyWidth);
        if(oTextarea.scrollWidth > nEmptyWidth)
        {
            let oldVal      = oTextarea.value;
            let newVal      = oldVal.substring(0, (oldVal.length - (curWord.length + 1))) + "\n" + curWord;
            oTextarea.value = newVal;
        }
    }
    oTextarea.setAttribute("wrap", "");

    return oTextarea.value;
};
0 голосов
/ 04 ноября 2013

Код проверен на «Crome», «Firefox» и «IE». Получить перевод строки и каретка возвращает компонент "textArea" ( на стороне клиента с javascript )

РАБОТАЕТ ХОРОШО! Я хотел бы поделиться этим с вами

важно включить стиль

    <html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<%--//definimos un estilo--%>
 <STYLE type="text/css">
    #CadTemp 
    {
        font-family: "Arial", serif; 
        font-size: 12pt; 
        visibility: hidden;
        position: absolute;
        top: -100;
        left: 0px;
    }
 </STYLE>

    <script type="text/javascript">
        function mostrar() {
            return 'ancho cadena: ' + document.getElementById('myTextarea').value.visualLength() + '  \n' + 'ancho textarea: ' + (document.getElementById('myTextarea').scrollWidth -4);
        }


        //sustituimos el espacio en blanco por el punto, tienen exactamente el mismo tamaño en 'pixeles'
        function reemplazarEspacios(texto) {
            var devolver = "";
            for (var i = 0; i < texto.length; i++) {
                if (texto.charAt(i) == ' ') {
                    devolver += '.'
                } else {
                    devolver += texto.charAt(i);
                }
            }
            return devolver;
        }

        // Calcula los pixeles de ancho que ocupa un texto (la cadena debe tener el mismo tamaño y tipo de fuente)
        String.prototype.visualLength = function () {
            var ruler = document.getElementById("CadTemp");            
            ruler.innerHTML = reemplazarEspacios(this)
            return ruler.offsetWidth;
        }

        //quitar espacios a la derecha de la cadena
        String.prototype.rtrim = function() {return this.replace(/\s+$/,"");}

        //devuelve el ultimo espacio de la cadena (que no sea espacio final)
        function IndEspacio(cadena) {
            //quito los espacios al final
            var cadenaTemp = cadena.rtrim();
            return cadenaTemp.lastIndexOf(' ');
        }

        //insertar un salto de linea
        function AplicarSaltosLinea(ID_elemento) {
           //guardo el elemento web en una variable
           var TextArea = document.getElementById(ID_elemento);
           var cadenaTexto = "";
           var Cadenafinal = "";
           var buffer = "";

           //recorremos toda la cadena
           for (var i = 0; i < TextArea.value.length; i++) {
                //guardamos el caracater en la cadena
                cadenaTexto += TextArea.value.charAt(i);

                //si hay un retorno de carro, antes de llegar al final del textArea
                if (TextArea.value.charAt(i) == '\n') {
                    Cadenafinal += cadenaTexto.substr(0, cadenaTexto.lastIndexOf('\n') + 1) ;
                    cadenaTexto = "";
                }

                //si el ancho actual de la cadena  es mayor o igual que el ancho del textarea (medida pixeles)
                if (cadenaTexto.visualLength() > TextArea.scrollWidth - 4) {
                    //recuperamos el ultimo espacio en blanco antes de la ultima letra o palabra
                    var indiceEspacio = IndEspacio(cadenaTexto)

                    buffer = "";
                    //ultimo espacio en blanco detectado, metemos el trozo de palabra desde el ultimo espacio
                    if (indiceEspacio >= 0) {
                        for (var j = indiceEspacio + 1; j <= i; j++)
                            buffer += cadenaTexto.charAt(j);
                        indiceEspacio = -1;
                    } else {
                        buffer += TextArea.value.charAt(i);
                    }
                    //coloca la cadena 
                    Cadenafinal += cadenaTexto.substr(0, cadenaTexto.length - buffer.length) + "\n";
                    cadenaTexto = buffer;
                }
            }

            Cadenafinal += cadenaTexto;

            document.getElementById("pnlPreview").innerHTML = Cadenafinal.replace(new RegExp("\\n", "g"), "<br />");
        }


    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <span id="CadTemp">hola</span>
    </div>
    <br />
    <div>
        <textarea cols="20" rows="5" id="myTextarea" wrap="hard" 
            style="font-family: Arial, Helvetica, sans-serif; font-size: 12pt"></textarea>
    </div>
    <div id="pnlPreview"></div>
    <div>
        <button type="button" onclick="AplicarSaltosLinea('myTextarea');">Apply Line Breaks</button>
        <button type="button" onclick="alert( document.getElementById('myTextarea').value )">mensaje</button>
        <button type="button" onclick="alert( mostrar())">calcular Ancho Pixel</button>
        <br />
        </div>
    </form>
</body>
</html>
...