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

В статье Использование шрифтов с пиктограммами при разработке приложений в Lazarus IDE приводится один из возможных вариантов использования векторной графики для улучшения качества отображаемых пиктограмм. Но если вы хотите полностью контролировать внешний вид того, что отрисовывается на форме, иметь возможность изменять цвета и параметры прозрачности нужных элементов, добавлять или удалять элементы изображения по мере необходимости, то в приложении нужно реализовывать вывод графики на форму из файла формата SVG.

Например, вы можете создать схематическое изображение изделия в вектором формате. С помощью элементов ввода приложения, таких как флажки или поля ввода, которые ассоциированы с моделью данных, изменять какие-либо его параметры. А на события получения фокуса или изменения параметров обновлять изображение, и пользователь сможет моментально увидеть, какой параметр он сейчас изменяет или как изменения повлияют на определённые конечные свойства изделия.

Использование векторного изображение для иллюстрации
Использование векторного изображение для иллюстрации

Такое редактирование изображения "на лету" возможно, поскольку SVG формат представляет собой документ на основе XML разметки. Внутри документа, с помощью тегов и их атрибутов, определены примитивы, которые и отрисовываются на холсте. Обрабатывая дерево XML документа можно искать любой тег или атрибут, изменять существующие и даже добавлять новые теги при необходимости, тем самым быстро корректировать изображение в соответствии с изменениями, которые вносятся пользователем при редактировании данных.

Подготовка изображения

Для формирования изображения SVG формата можно использовать свободно распространяемый векторный графический редактор Inkscape. Но если использовать различные инструменты этого редактора, то в структуру файла будут вноситься дополнительные данные и возможно будут использоваться теги и атрибуты, которые не поддерживаются библиотеками работы с SVG в Lazarus IDE. По этой причине можно рекомендовать использовать базовые инструменты Inkskape для отрисовки векторных изображений, а в дальнейшем корректировать файл с помощью текстового редактора.

Небольшие изображения в принципе можно создавать с помощью текстового редактора. Графические элементы, описываемые путём (path), который содержит последовательность команд, определяющих форму, положение и преобразование графики, можно создавать с помощью простого онлайн-инструмента Svg-path-editor. Он позволяет создавать и редактировать SVG-пути, при этом в панели команд наглядно отображаются все параметры команды, а выбранная команда подсвечивается в области отображения пути.

Панель команд и подсветка участка, формируемого командой в Svg-path-editor
Панель команд и подсветка участка, формируемого командой в Svg-path-editor

Для удобства обработки изображения в процессе исполнения приложения, необходимо группировать графические теги примитивов и присваивать группе и каждому тегу идентификатор, по которому его можно будет найти. Например, в документе можно будет найти указать тег <tspan id="di_tspan" ..., что позволит найти и подсветить обозначение размера на изображении. Для этого нужно изменить размер и цвет выводимого текста, тем самым выделяя его среди остальных.

Группировка тегов для вывода размера на рисунке:

<g id="top_dim1">
    <path id="dim1" d="M 363,212 l -201.5,-140 h -50"/>
    <use xlink:href="#arrow_down_right" x="362" y="211.5" id="arr1_dim1"/>
    <use xlink:href="#arrow_up_left" x="240" y="126.5" id="arr2_dim1"/>
    <text id="text_dim1" x="125" y="64">
        <tspan id="di_tspan" fill="#000000" stroke="none" font-size="20px">D</tspan>
        <tspan id="di_tsubspan" fill="#000000" stroke="none" font-size="10px">i</tspan>
    </text>
</g>

Использование изображения в приложении

В Lazarus IDE есть несколько библиотек для отображения SVG на элементах формы, среди них можно отметить FPVectorial и BGRABitmap.

FPVectorial предлагает поддержку чтения, изменения и записи векторных изображений. Хотя библиотека больше не развивается, сообщество продолжает её поддерживать. С выходом версии 3 Lazarus IDE в библиотеке были исправлены ошибки, приводившие к утечкам памяти, что значительно повысило стабильность её работы. Библиотека входит в состав дистрибутива IDE.

BGRABitmap - это достаточно большая библиотека, которая продолжает развиваться и имеет широкий набор готовых компонентов. На WIKI страничке базы знаний по Free Pascal и Lazarus есть опубликованные уроки по использованию BGRABitmap. Чтобы воспользоваться библиотекой, установите её через сетевой диспетчер пакетов, после чего в панели компонентов появятся вкладки с компонентами BGRABitmap.

Общая схема работы с SVG

Для работы с любой из библиотек её необходимо добавить в зависимости проекта через Инспектор проекта. Для этого выполните следующие шаги:

  1. Откройте Инспектор проекта (Проект → Инспектор проекта...).

  2. Выберите пункт "Требуемые пакеты".

  3. Через контекстное меню вызовите окно добавления новой зависимости.

  4. Выберите и добавьте нужный пакет.

Выбранная библиотека, влияет на алгоритм работы с документом, так как FPVectorilal поддерживает загрузку данных из TXMLDocument, а TBGRASVG нет.

Алгоритм работы с SVG изображением, в зависимости от выбранной библиотеки
Алгоритм работы с SVG изображением, в зависимости от выбранной библиотеки

Обработка TXMLDocument

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

procedure MarkText(const ATegID:string; ADoc:TXMLDocument);
var
  nodeList:TDomNodeList;
  tspanNode:TDomNode;
  tspanId:String;
  i:integer;
begin
  nodeList:=ADoc.GetElementsByTagName('tspan');
  for i:=0 to nodeList.Count-1 do
    begin
    tspanNode:=nodeList.Item[i];
    tspanId := UTF8Encode(TDOMElement(tspanNode).GetAttribute('id'));
    if (tspanId = ATegID+'_tspan') then
     begin
       TDOMElement(tspanNode).SetAttribute('fill', '#ff0000');
       TDOMElement(tspanNode).SetAttribute('font-size', 30.ToString+'px');
     end;
    if (tspanId = ATegID+'_tsubspan') then
     begin
       TDOMElement(tspanNode).SetAttribute('fill', '#ff0000');
       TDOMElement(tspanNode).SetAttribute('font-size', 20.ToString+'px');
     end;
    end;
end; 

Создании копии XML документа:

 //Чтение SVG в структуру
  ReadXMLFile(SourceImageXMLDoc,ImageFileName); 
    
 // Создание копии
  SourceImageDocumentNode:=SourceImageXMLDoc.DocumentElement;
  TargetImageXMLDoc:=TXMLDocument.Create;
  TargetImageDocumentNode:=TargetImageXMLDoc.ImportNode(SourceImageDocumentNode,true);
  TargetImageXMLDoc.AppendChild(TargetImageDocumentNode);  

Поиск и удаление узлов в структуре по идентификатору:

 tmpNodeList:=TargetImageDocumentNode.GetElementsByTagName('g');
 for i:=0 to tmpNodeList.Count-1 do
  begin
    groupNode:=tmpNodeList.Item[i];
    groupNodeID := UTF8Encode(TDOMElement(groupNode).GetAttribute('id'));
    if (groupNodeID = 'top_bars') then
      topBarNode:=groupNode;
    if (groupNodeID = 'front_top_bars') then
      frontBarNode:=groupNode;
    end;
    if Assigned(topBarNode) then
    begin
      parentNode := topBarNode.ParentNode;
      if Assigned(parentNode) then
        parentNode.RemoveChild(topBarNode);
    end;
    if Assigned(frontBarNode) then
    begin
      parentNode := frontBarNode.ParentNode;
      if Assigned(parentNode) then
        parentNode.RemoveChild(frontBarNode);
    end;  
  end;

Выделения текста:

procedure MarkText(const ATegID:string; ADoc:TXMLDocument);
var
  nodeList:TDomNodeList;
  tspanNode:TDomNode;
  tspanId:String;
  i:integer;
begin
  nodeList:=ADoc.GetElementsByTagName('tspan');
  for i:=0 to nodeList.Count-1 do
    begin
    tspanNode:=nodeList.Item[i];
    tspanId := UTF8Encode(TDOMElement(tspanNode).GetAttribute('id'));
    if (tspanId = ATegID+'_tspan') then
     begin
       TDOMElement(tspanNode).SetAttribute('fill', '#ff0000');
       TDOMElement(tspanNode).SetAttribute('font-size', 30.ToString+'px');
     end;
    if (tspanId = ATegID+'_tsubspan') then
     begin
       TDOMElement(tspanNode).SetAttribute('fill', '#ff0000');
       TDOMElement(tspanNode).SetAttribute('font-size', 20.ToString+'px');
     end;
    end;
end; 

Заключение

Хотя Lazarus IDE не является распространённой средой разработки, она отлично подходит для быстрого создания прототипов или небольших приложений с интерфейсом. Сами же подходы и инструменты, описанные в статье, могут быть полезны при разработке приложений в любой среде разработки. И если вы найдете в статье что-то полезное для себя, это будет отличным результатом данной публикации.

Комментарии (3)


  1. Qlavrt
    30.12.2024 13:54

    Что разрабатываете?


    1. Avlakan Автор
      30.12.2024 13:54

      Обычно разрабатываю всякие мелкие утилиты. В статье сделал просто пример на основе эскиза трансформатора тока.


  1. Avlakan Автор
    30.12.2024 13:54

    О чем хотите прочитать в продолжении?