Главная » Статьи » Lazarus

RichMemo: вставка таблиц и ссылок

1. Предварительные замечания

2. Подготовка и вставка таблиц в текст RTF

3. Вставка ссылки в текст RTF


1. Предварительные замечания

В прошлом году я скачал  с сайта Журнал "Самиздат" произведения Михаила Харитонова в формате FB2. Как выяснилось практически все файлы содержали ошибки в структуре XML и FB2. Открывать их отказались не только программы чтения, но и редактор fb2edit.exe. Так как я уже пытался работать с форматом FB2 при формировании файловой структуры своей локальной библиотеки на десктопе, то решил "сваять"  свой редактор FB2, используя FPC / Lazarus.

Постепенно редактор оброс множеством функций и дополнений, и, в конце концов, было решено дополнить возможностью форматированного просмотра текста FB2. В качестве основы был выбран компонент RichMemo, который в ОС Windows построен на базе элемента управления Rich Edit.

Компонент RichMemo позволил достаточно просто управлять параметрами шрифтов, выравниванием и метриками абзацев, вставку в текст изображений. Однако некоторые затруднения вызвала необходимость размещения в тексте таблиц и работа с ссылками. Далее описываются некоторые результаты, полученные мной в процессе работы. Все изложенное ниже проверялось только при работе в ОС Windows, т.е. в случае работы с системным элементом Rich Edit.

Как известно, элемент Rich Edit работает с текстами в формате RTF (Rich Text Format), поэтому чтобы обеспечить полноценную работу с фрагментами текста необходимо формировать и вставлять эти фрагменты именно в формате RTF. Кроме этого текстовые фрагменты должны иметь кодировку Windows-1251 или кодами в Юникоде. По умолчанию в Lazarus принята кодировка UTF-8, поэтому требуются некоторые усилия для обеспечения правильной кодировки.

Для вставки фрагментов в формате RTF дополняем класс TRichMemo следующим helper'ом:

type

  { TRichMemoHelper }

  TRichMemoHelper = class helper for TRichMemo
    procedure InsertRTFOverClipBoard(const s: T1251String);
  end;

{ TRichMemoHelper }

procedure TRichMemoHelper.InsertRTFOverClipBoard(const s: T1251String);
var ss: TStringStream;
begin
  //передача RTF через clipboard
  ss := TStringStream.Create(s, TEncoding.ANSI);
  try
    Clipboard.SetFormat(RegisterClipboardFormat(CF_RTF), ss);
    PasteFromClipboard;
  finally
    ss.Free;
  end;
end;

Тип строки T1251String предназначен для автоматической перекодировки UTF-8Windows-1251. Объявление этого типа имеет следующий вид:

type T1251String = Type AnsiString(1251);

 

2. Подготовка и вставка таблиц в текст RTF

Для описания таблиц в документах FB2 используется код XML очень похожий на упрощенное описание таблиц в HTML.

Элемент <table> содержит контейнеры для создания строк, которые задаются тегом <tr>. Для этого тега может быть задан атрибут align, определяющий выравнивание  содержимого по горизонтали в ячейках строки.

Каждая ячейка таблицы в пределах строки задается с помощью тегов <td> или <th>. Для этих тегов могут быть заданы атрибуты:

  • rowspan и colspan — определяют объединение вертикальных и горизонтальных ячеек;
  • align — определяет горизонтальное выравнивание  содержимого ячейки, данный атрибут перекрывает такой же атрибут, установленный для тега <tr>;
  • valign — определяет выравнивание  по вертикали содержимого ячейки.

С тегом <th> создаются ячейки, которые обозначаются как заголовочные. Текстовое содержимое этих ячеек выделяется соответствующим форматированием, например, жирным шрифтом.

Приведенный выше XML код определяет таблицу следующего вида:

№ по порядку Вид ресурса
Изображения Текст Прочие
1 pic1.jpg Рис. 1  
2 pic23.jpg Рис. 2 icon1

Теперь рассмотрим, как должен быть сформирован RTF текст, который позволяет вставить в TRichMemo рассмотренную выше таблицу.  Следует отметить, что при формировании RTF текста все размеры устанавливаются в твипах (twips) равных одной двадцатой пункта или 1/1440 дюйма, примерно 0,0176 мм.

Предполагается, что таблица содержит только текстовые данные, которые отображаются шрифтом одинаковой гарнитуры. Для дополнительного форматирования могут применяться следующие теги:

  • <strong> — установка полужирного (bold) начертания;
  • <emphasis> — установка курсивного (italic) начертания;
  • <strikethrough> — установка перечеркнутого начертания;
  • <sup><sub> — установка положения верхнего и нижнего индекса, соответственно.

Приведем с описанием элементы текста с привязкой к исходному XML коду. Более подробно о формате RTF текста версии 1.0 можно, например, здесь.

{\rtf1\ansi\ansicpg1251 — заголовок RTF v.1.0, набор символов ANSI и кодовая страница Windows 1251;
\trowd\trgaph50\trleft400 — начало строки таблицы, расстояние между ячейками строки и позиция левого края строки таблицы;
\clvmgf — индикация первой ячейки для вертикального объединения, учет атрибута rowspan;
\clbrdrt\brdrw10\clbrdrl\brdrw10\clbrdrb\brdrw10\clbrdrr\brdrw10 — установка толщины рамки в 10 твипов для верхней, левой нижней и правой границ ячейки, соответственно;
\cellx2038 — установка координаты правой границы ячейки в твипах;
\clbrdrt\brdrw10\clbrdrl\brdrw10\clbrdrb\brdrw10\clbrdrr\brdrw10\cellx11400 — установка параметров рамки и правой координаты второй ячейки. Факт, что для этой ячейки установлен атрибут colspan, учитывается увеличением правой координаты на сумму ширин объединяемых ячеек.
\pard\intbl — сброс параметров параграфа и индикация, что параграф является частью таблицы;
\qc \b № по порядку\b0\cell — определение содержимого ячейки с учетом кодов форматирования: \qc — выравнивание по центру; \b, \b0 — включение и выключение жирного шрифта; \cell — конец ячейки;
\qc \b Вид ресурса\b0\cell
\row — конец строки таблицы;
\trowd\trgaph50\trleft400 — следующая строка;
\clvmrg — индикация, что ячейка объединена с предыдущей по вертикали;
\clbrdrt\brdrw10\clbrdrl\brdrw10\clbrdrb\brdrw10\clbrdrr\brdrw10\cellx2038
\clbrdrt\brdrw10\clbrdrl\brdrw10\clbrdrb\brdrw10\clbrdrr\brdrw10\cellx6017
\clbrdrt\brdrw10\clbrdrl\brdrw10\clbrdrb\brdrw10\clbrdrr\brdrw10\cellx8591
\clbrdrt\brdrw10\clbrdrl\brdrw10\clbrdrb\brdrw10\clbrdrr\brdrw10\cellx11400
\pard\intbl
\cell
\ql \i Изображения\i0 \cell
— содержимое с форматированием: \ql — выравнивание влево; \i, \i0 — включение и выключение курсива;
\ql \i Текст\i0 \cell
\ql \i Прочие\i0 \cell
\row
\trowd\trgaph50\trleft400
\clbrdrt\brdrw10\clbrdrl\brdrw10\clbrdrb\brdrw10\clbrdrr\brdrw10\cellx2038
\clbrdrt\brdrw10\clbrdrl\brdrw10\clbrdrb\brdrw10\clbrdrr\brdrw10\cellx6017
\clbrdrt\brdrw10\clbrdrl\brdrw10\clbrdrb\brdrw10\clbrdrr\brdrw10\cellx8591
\clbrdrt\brdrw10\clbrdrl\brdrw10\clbrdrb\brdrw10\clbrdrr\brdrw10\cellx11400
\pard\intbl
\qc 1\cell
\qr pic1.jpg\cell
\qr Рис. 1\cell
\qr \cell
\row
\trowd\trgaph50\trleft400
\clbrdrt\brdrw10\clbrdrl\brdrw10\clbrdrb\brdrw10\clbrdrr\brdrw10\cellx2038
\clbrdrt\brdrw10\clbrdrl\brdrw10\clbrdrb\brdrw10\clbrdrr\brdrw10\cellx6017
\clbrdrt\brdrw10\clbrdrl\brdrw10\clbrdrb\brdrw10\clbrdrr\brdrw10\cellx8591
\clbrdrt\brdrw10\clbrdrl\brdrw10\clbrdrb\brdrw10\clbrdrr\brdrw10\cellx11400
\pard\intbl
\qc 2\cell
\qr pic23.jpg\cell
\qr Рис. 2\cell
\qr icon1\cell
\row
}

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

Пример фрагмента кода для вставки сформированной строки:

//установка параметров форматирования для таблицы
st := UTF8Length(FRichMemo.Text); //позиция вставки таблицы - здесь в конце текста
FRichMemo.InDelText(' ', st, 0); //вставляем пробел
FRichMemo.SetTextAttributes(st, 1, TableFormatParams.font); //устанавливаем параметры шрифта по умолчанию
FRichMemo.SetParaAlignment(st, 1, TableFormatParams.alignm); //устанавливаем выравнивание по умолчанию
FRichMemo.SelStart := st; FRichMemo.SelLength := 1; //выделяем  вставленный в текст пробел

//передаем через clipboard RTF, сформированный buf — строка ранее описанного типа T1251String
FRichMemo.InsertOverClipBoard(buf);

 

3. Вставка ссылки в текст RTF

Компонент RichMemo позволяет размещать в тексте гиперссылки. Для этого имеется следующий метод:

procedure TCustomRichMemo.SetLink(TextStart, TextLength: Integer; AIsLink: Boolean; const ALinkRef: String = ''); virtual;

где TextStart, TextLength — определяют позицию в тексте, которой назначается/удаляется гиперссылка; AIsLink — равен true при установке гиперссылка, и false – при удалении; ALinkRef — текст гиперссылки.

Событие выбора пользователем гиперссылки обрабатывается в обработчике OnLinkAction, который должен быть назначен на метод следующего типа:

TLinkActionEvent = procedure (Sender: TObject; ALinkAction: TLinkAction; const info: TLinkMouseInfo; LinkStart, LinkLen: Integer) of object;

где ALinkAction — тип события, единственное значение laClick; info — запись с двумя полями: info.Button — значения mbLeft или mbRight; info.LinkRef —  текст гиперссылка; LinkStart, LinkLen — позиция гиперссылки в тексте.

Тестирование работы с гиперссылками в компоненте RichMemo показало, что при назначении текста гиперссылки через параметр ALinkRef в методе SetLink, в обработчике OnLinkAction значение поля info.LinkRef всегда равно строковому значению фрагмента текста, определяемого параметрами TextStart и TextLength в методе SetLink.

Анализ кода реализации RichMemo для Windows показал, что значение параметра ALinkRef вообще не используется. Признак гиперссылки производится лишь установкой флага CFE_LINK для фрагмента текста, определяемого параметрами TextStart и TextLength в методе SetLink.

Как указано на сайте Microsoft, для определения гиперссылки с понятным именем (Friendly Name Hyperlinks) необходимо вставить соответствующий текст RTF:

{\rtf1\ansi\ansicpg1251{\field{\*\fldinst{ HYPERLINK "http://www.msn.com"}}{\fldrslt{ MSN}}}},

где http://www.msn.com — адрес гиперссылки; MSN — отображаемое в тексте имя  гиперссылки.

Пример фрагмента кода для вставки гиперссылки c адресом hrefAddr и отображаемым текстом hrefText:

const cRTFLinkTextTemplate:string =
          '{\rtf1\ansi\ansicpg1251{\field{\*\fldinst {HYPERLINK "%buf" }}{\fldrslt {%buf}}}}';
var buf: T1251String;

st := UTF8Length(FRichMemo.Text); //позиция вставки гиперссылки - здесь конец текста
FRichMemo.InDelText(' ', st, 0)(' '); //вставляем пробел
FRichMemo.SelStart := st; FRichMemo.SelLength := 1; //выделяем вставленный пробел
FRichMemo.SetTextAttributes(st, 1, FFormatStack.Top.font); //установка параметров форматирования, если необходимо

buf := Format(pRTFLinkText, [hrefAddr, hrefText]); //формирование текста в RTF

FRichMemo.InsertOverClipBoard(buf); //передача RTF через clipboard

Отображаемый текст hrefText может содержать теги форматирования, аналогично ранее описанных для текстовых данных таблицы.

 

В начало

Категория: Lazarus | Добавил: zoleg5763 (01.04.2021)
Просмотров: 627 | Рейтинг: 0.0/0
Всего комментариев: 0
avatar