Russian Qt Forum
Март 28, 2024, 18:59 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Метод QTextEdit::insertHtml() съедает ведущие пробелы. Как обойти?  (Прочитано 5461 раз)
xintrea
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« : Май 22, 2016, 21:31 »

Решаю сейчас в своем WYSIWYG-редакторе следующую задачу:

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

Задача:
Написать функцию (метод), очищающий форматирование выделенного текста в виджете-наследнике QTextEdit.
https://github.com/xintrea/mytetra_dev/issues/5

То есть, нужно изменить только начертание символов. Ссылки должны остаться ссылками. Картинки должны остаться картинками. Таблицы должны остаться таблицами. Изменяется только начертание.

При кажущейся простоте, подводных камней у этой задачи много. Сейчас я почти достиг цели - написал жуткий magick-код, который берет HTML-код выделенного фрагмета текста, обрабатывает его (убирает лишнее, подменяет теги, компенсирует многочисленные закидоны Qt-овского HTML-движка), и вставляет его обратно.

Такая методика наконец-таки заработала чисто, за одним маленьким исключением: при вставке HTML-кода через метод insertHtml(), съедаются ведущие пробелы в тексте. То есть, если в тексте встретится кусок исходного кода, то indentation у него слетит.

Вот пример HTML-кода до вставки и что реально вставилось:

Вставляли:
Код:
<p style="margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;"><span style="margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;">    QMessageBox msgBox;</span></p>
<p style="margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;"><span style="margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;">    msgBox.setText(welcomeText);</span></p>
<p style="margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;"><span style="margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;">    msgBox.setInformativeText(infoText);</span></p>

Получили:
Код:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><!--StartFragment-->QMessageBox msgBox;</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">msgBox.setText(welcomeText);</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">msgBox.setInformativeText(infoText);<!--EndFragment--></p></body></html>

Видно, что ведущие четыре пробела в каждой строчке исчезли.

Код, который это делает:

Код:
...
  qDebug() << htmlCode;

  // Вставка очищенного фрагмента
  textArea->textCursor().insertHtml(htmlCode);

  // Выделение только что вставленного фрагмента (это тоже магия)
  cursor.movePosition(QTextCursor::End);
  int afterClearLen=cursor.position();
  int calculateEndCursorPos=startCursorPos + (afterClearLen - afterRemoveSelectionLen);
  cursor.setPosition(startCursorPos, QTextCursor::MoveAnchor);
  cursor.setPosition(calculateEndCursorPos, QTextCursor::KeepAnchor);
  textArea->setTextCursor(cursor);

  qDebug() << textArea->textCursor().selection().toHtml();
...

Вопрос: как можно обойти эту особенность Qt, чтобы в данном случае все пробелы сохранялись?

Я уже готов выбрать редко используемый UTF-8 символ (какой-нибудь иероглиф), заменить им все пробелы во вставляемом HTML, а потом заменить в документе эти символы на пробелы. Или не иероглиф, а нигде-никогда-не-встречаемую-строку. Но это же все неправильно (вдруг иероглиф кто-то использует, или так получится, что уникальная строка таки встретится). А хотелось бы правильно.

А как правильно?


Последний актуальный коммит: https://github.com/xintrea/mytetra_dev/commit/11bde04320971b28420f1fba217ad0ed438d43da
Записан

Собираю информацию по крупицам
http://webhamster.ru
gil9red
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 1805



Просмотр профиля WWW
« Ответ #1 : Май 22, 2016, 21:43 »

html к пробелам не очень хорошо относится

заменяйте пробелы строкой "&nbsp;"
Записан

xintrea
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« Ответ #2 : Май 22, 2016, 22:40 »

html к пробелам не очень хорошо относится

заменяйте пробелы строкой "&nbsp;"

Вот сам подумай. Ты предлагаешь заменить пробелы на неразрывные пробелы. А потом их нужно поменять на обычные пробелы. Вопрос. Как ты отличишь неразрывные пробелы, которые внесены были искусственно от неразрывных пробелов, которые изначально были в тексте?

И второе. Чтобы твой метод проверить, нужно решить другую задачу: как в HTML поменять пробел " " на на неразрывный пробел «амперсандnbsp;» так, чтобы захватились только пробелы в тексте. А пробелы в тегах и в атрибутах не затронулись.

Что-то я не могу составить такую заковыристую регулярку. В Qt в QRegEx еще есть проблема обработки многострочных строк - если регулярка попадет на перенос строки, она не отработает.
Записан

Собираю информацию по крупицам
http://webhamster.ru
xintrea
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« Ответ #3 : Май 23, 2016, 00:20 »

html к пробелам не очень хорошо относится

заменяйте пробелы строкой "&nbsp;"

Решил сделать по-честному: распарсить HTML через DOM XML, и заменить пробелы на "&nbsp;" только в текстовых узлах.

Загружаю HTML-код в QDomDocument. Прочесываю его рекурсивно такой функцией:

Код:
void TypefaceFormatter::recurseReplaceSpaces(const QDomNode &node)
{
  QDomNode domNode = node.firstChild();
  QDomText domText;

  // Если текущий элемент существует
  while(!(domNode.isNull()))
  {
    // Если узел - это текст
    if(domNode.isText())
    {
      QDomText domText = domNode.toText();
      if(!domText.isNull())
      {
        QString text=domText.data();
        text.replace(" ", "&nbsp;");
        qDebug() << "Replace spaces: " << text;

        // В узле устанавливается новая строка
        domNode.setNodeValue(text);
      }
    }

    recurseReplaceSpaces(domNode);
    domNode = domNode.nextSibling();
  }
}

В ходе ее работы вижу:

Код:
00:10:33 [DBG] Replace spaces:  "&nbsp;&nbsp;&nbsp;QMessageBox&nbsp;msgBox;"
00:10:33 [DBG] Replace spaces:  "&nbsp;&nbsp;&nbsp;msgBox.setText(welcomeText);"
00:10:33 [DBG] Replace spaces:  "&nbsp;&nbsp;&nbsp;msgBox.setInformativeText(infoText);"

Казалось бы, вопрос решен? Нет!

Когда генерируем HTML из этого DOM-дерева через метод doc.toString(), символ «&» заменяется на «&amp;», и мы получаем каку:

Код:
<p style="margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;">
  <span style="margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;">&amp;nbsp;&amp;nbsp;&amp;nbsp;QMessageBox&amp;nbsp;msgBox;</span>
 </p>
 <p style="margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;">
  <span style="margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;">&amp;nbsp;&amp;nbsp;&amp;nbsp;msgBox.setText(welcomeText);</span>
 </p>
 <p style="margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;">
  <span style="margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;">&amp;nbsp;&amp;nbsp;&amp;nbsp;msgBox.setInformativeText(infoText);</span>
 </p>

В какой момент происходит эта подмена, понять не могу. То ли сразу при установке значения текстового узла, то ли при генерации doc.toString().

Факт в том, что из DOM не могу получить HTML с «&nbsp;». А это ключевое ключевое действие, которое надо сделать.
Записан

Собираю информацию по крупицам
http://webhamster.ru
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.051 секунд. Запросов: 22.