Генерация PDF документов в Lazarus IDE

от автора

Для разработки различных заглушек, используемых для тестирования сервиса, пока не готова ответная часть, я иногда использую отрисовку нужной информации на Canvas PaintBox`а, и последующую генерацию PDF с отрисованной картинкой (сохраняю BMP в поток, затем загрузка из потока для размещения изображения в PDF) и дополнением документа текстовой информацией. Для реализации такого подхода я использую модуль fpPDF, который, на мой взгляд, является достаточно простым и удобным. Для ознакомления с возможностями модуля ниже привожу вольный перевод статьи разработчика данного модуля — Michaël Van Canneyt Creating PDF files in Lazarus and Free Pascal.

Введение

В Lazarus уже давно есть компонент для создания PDF: PowerPDF. Он был перенесен из Delphi и используется в LazReport для создания PDF-файлов из отчетов. Однако у него есть несколько недостатков: он требует подключения LCL и графического интерфейса и не поддерживает встраивание шрифтов и шрифты юникода.

Чтобы исправить это, недавно во Free Pascal была добавлена новая реализация: fpPDF. Этот новый компонент имеет следующие особенности:

  • Чистый код на Object Pascal.

  • Отсутствие зависимостей от графического интерфейса, внешних библиотек или вызовов ОС.

  • Множество примитивов для рисования :- Изображения (любой формат, поддерживаемый FPC)

    • Линии (стили пера)

    • Круги

    • Прямоугольники (опционально закругленные)

    • Полилинии, т.е. рисование нескольких сегментов линии одной командой.

    • Многоугольники, с двумя режимами заливки: ненулевое правило намотки и четно-нечетное правило заливки.

    • Поддержка кривых Безье.

  • Полная поддержка TTF.

  • Поддержка Юникода.

  • Опциональное встраивание шрифтов (частичное встраивание находится в стадии разработки).

  • Поддержка преобразования координат (вращение, масштабирование и перевод).

  • Поддержка сжатия текста и изображений.

  • Встраивание HTML-ссылок

Поскольку этот компонент не требует графического интерфейса, он очень удобен для использования, например, в веб-приложениях, где графического интерфейса и нет. Так как компонент не имеет внешних зависимостей и самостоятельно обрабатывает шрифты, его можно использовать на любой платформе, которую поддерживают Free Pascal и Lazarus, включая различные вариации Raspberry Pi. Новый Lazarus lazReport PDF export уже был создан с помощью данного компонента.

Использовать этот компонент достаточно просто, что и будет продемонстрированно далее. Код достаточно прост, чтобы без особых усилий перенести компонент на Delphi, если это понадобится.

2. Установка компонента

В оригинале статьи описывается вариант установки компонента через скачивание и установку пакета lazpdf.lpk, но в последних версиях Lazarus fpPDF уже входит в дистрибутив IDE. Для использования компонента достаточно добавить в список используемых модулей fpPDF (uses fppdf;).

Основной класс из модуля pfPDF — это TPDFDocument, который имеет несколько опубликованных свойств:

  • LineStyles — коллекция стилей линий, которые можно использовать при рисовании. Стиль линии определяет цвет, толщину линии и стиль пера.

  • PageLayout — это свойство определяет, как программа просмотра PDF должна открывать файл: отображать одну страницу, две страницы или всё в непрерывном режиме.

  • Infos — это свойство содержит список свойств документа, которые можно задать (разработчик, автор и т. д.). Они будут отображаться в свойствах документа в средстве просмотра PDF-файлов.

  • DefaultPaperType — тип бумаги по умолчанию, используемый при создании новой страницы. По умолчанию установлено значение A4.

  • DefaultOrientation — тип ориентации по умолчанию для новой страницы альбомная или портретная.

  • DefaultUnitOfMeasure — это свойство определяет единицу измерения по умолчанию, используемую на страницах PDF-документа. Это может быть одно из следующих значений: дюймы, миллиметры (по умолчанию), сантиметры или пиксели (дополнительная информация по этому вопросу будет приведена ниже).

  • FontDirectory — папка расположения всех шрифтов, которые необходимо внедрить в документ.

  • Options — набор параметров, которые могут быть заданы и могут влиять на тип генерируемого PDF-кода. В настоящее время доступны следующие варианты опций:

    • poOutLine — создать структуру документа, которая будет отображаться в средстве просмотра PDF.

    • poCompressText — сжать все строки, записанные в документ.

    • poCompressFonts — сжать встроенные шрифты.

    • poCompressImages — сжать изображения, включенные в документ.

    • poUseRawJPEG — при добавлении изображения JPEG изображение включается как есть. По умолчанию читать изображение и преобразовывать его в собственный формат PDF.

    • poNoEmbeddedFonts — не встраивать шрифты в документ. По умолчанию все шрифты в документ встраиваются.

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

3. Рисование: система координат

Стандарт PDF является производным от стандарта PostScript. Это означает, что начало холста рисования находится в нижнем левом углу страницы, как показано на рисунке 1. Большее значение координаты Y означает более высокую позицию на странице.

Habr-4-1.png

Рис.1. Система координат PDF

Это отличается от системы координат, используемой в Canvas приложений LCL или VCL, где начало координат находится в верхнем левом углу, и где более высокие значения координаты Y означают более низкие значения на экране. Систему координат можно сделать совместимой со значением LCL или VCL, указав опцию poPageOriginAtTop в наборе параметров документа. После чего новая страница будет с новой системой координат, начинающейся в верхнем левом углу. Система координат текущей страницы не будет изменена.

Система координат использует значение с плавающей точкой и использует 2 цифры для точности. Она может использовать различные единицы, которые можно задать для каждой страницы отдельно в свойстве UnitOfMeasure для объекта страницы. Опция имеет одно из следующих значений:

  • uomInches — 1 дюйм равен 25,4 миллиметра.

  • uomMillimeters — миллиметры, единица по умолчанию.

  • uomCentimeters — сантиметры.

  • uomPixels — пиксели. Количество пикселей на дюйм рассчитывается с использованием DPI равным 72.

Единицу измерения по умолчанию можно указать на уровне документа. Все команды рисования сначала преобразуют координаты с помощью матрицы преобразования. Когда команда рисования имеет координаты (X, Y), то X и Y масштабируются и преобразуются следующим образом:

X := XScale * X + XTranslation; Y := YScale * Y + YTranslation;

Обратите внимание, что это не позволяет выполнять поворот, для которого требуется немного более сложная матрица. Вместо этого поворот должен быть указан отдельно при рисовании объектов.

4. Рисование: начало работы

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

TPDFDocument является «странично-ориентированным» компонентом. Это означает, что перед тем, как приступить к рисованию, необходимо инициировать запуск страницы. Страницу запускают вызовом метода Pages.AddPage, который возвращает объект TPDFPage. Затем эту страницу необходимо добавить в раздел.

Пример кода, который начинает документ и добавляет в него раздел и страницу:
var  D: TPDFDocument;  S: TPDFSection;  P :TPDFPage; begin      D:=TPDFDocument.Create(Nil); // Создание документа      D.StartDocument; // Начало работы с документом      S:=D.Sections.AddSection; // Добавление раздела. Помните, необходим хотя бы один раздел!      P:=P.Pages.AddPage;      S.AddPage(P);// Добавление страницы в раздел      // Установка свойств страницы:      P.PaperType := ptA4;      P.UnitOfMeasure := uomMillimeters; end;

В TPDFPage есть два способа указать размер страницы:

  1. Использование PaperType и Orientation, что установит размеры в свойстве Paper.

  2. Установка PaperType в значение ptCustom и явная установка размеров в свойстве Paper.

Свойство Paper содержит размер бумаги в точках PDF (пикселях). Чтобы преобразовать размеры PDF в миллиметры, сантиметры или дюймы, можно использовать процедуры преобразования mmToPDF, PDFToMM и т.д. названия процедур говорят сами за себя.

5. Рисование: цвета, ширина линий и стили

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

procedure SetColor(AColor : TARGBColor; AStroke : Boolean = True); procedure SetPenStyle(AStyle : TPDFPenStyle; ALineWidth: TPDFFloat = 1.0);

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

Цвета, используемые в документе PDF, указываются с использованием нотации RGB. Модуль fpPDF содержит некоторые константы для общих цветов. При установке цвета необходимо указать, является ли это цветом обводки (или рисования линии) (AStroke=True) или цветом заливки (AStroke=False). Стиль пера определяет, как будут нарисованы линии, и может быть одним из следующих:

 TPDFPenStyle = (ppsSolid,ppsDash,ppsDot,ppsDashDot,ppsDashDotDot);

Значение этих вариантов прямо отражено в названии. Чтобы упростить управление цветами и стилями линий, компонент TPDFDocument может поддерживать набор стилей линий в свойстве коллекции LineStyles. Каждый элемент в коллекции определяет цвет, ширину и стиль пера.

Для установки цвета, ширины и стиля пера в одной команде, можно использовать следующие методы:

procedure SetLineStyle(AIndex: Integer; AStroke: Boolean = True); procedure SetLineStyle(S: TPDFLineStyleDef; AStroke: Boolean = True);

Стиль можно задать используя AIndex элемента в свойстве документа LineStyles, или элемент можно указать напрямую.

6. Рисование: команды рисования линий

Класс TPDFPage во многом похож на класс TCanvas в LCL или VCL: он предлагает множество команд для фактического рисования чего-либо на странице. Существует 4 команды рисования линий:

procedure DrawLine(X1, Y1, X2, Y2, ALineWidth : TPDFFloat;                    const AStroke: Boolean = True); procedure DrawLine(APos1, APos2: TPDFCoord; ALineWidth: TPDFFloat;                    const AStroke: Boolean = True); procedure DrawLineStyle(X1, Y1, X2, Y2: TPDFFloat; AStyle: Integer); procedure DrawLineStyle(APos1, APos2: TPDFCoord; AStyle: Integer);

TPDFCoord — это запись, описывающая положение X и Y с использованием типа TPDFFloat. Все команды предоставляются в двух перегруженных формах: одна с явными координатами X и Y, другая с координатами, указанными через структуру TPDFCoord. В этом документе мы представим только первую форму.

Команда DrawLine нарисует линию в текущем цвете, стиле и ширине линии. Команда DrawLineStyle нарисует линию в цвете, стиле и ширине линии, заданных в указанном элементе свойства LineStyles документа TPDFDocument. Обратите внимание, что стиль останется установленным для последующих команд.

Опция AStroke, установленная в значение True, указывает PDF-движку на необходимость создания линии. Если необходимо нарисовать много сегментов линий, то линии могут быть нарисованы не кусками (каждая линия по отдельности), а как бы целиком (это называется рисованием пути), в этом случае, когда последняя линия будет задано, можно выполнить отрисовку контура (будет фактически нарисован заданный путь).

Пример кода, который рисует прямоугольник на странице:
procedure TMainForm.GenerateLines(P : TPDFPage); var  W,M,T,R : TPDFFloat; begin  W:=1.0; // Ширина страницы  M:=10; // Поле  T:=PDFToMM(P.Paper.H)-M; // Верх  R:=PDFToMM(P.Paper.W)-M; // Право  // Поля страницы  P.DrawLine(M,M,M,T,W,True);  P.DrawLine(M,M,R,M,W,True);  P.DrawLine(M,T,R,T,W,True);  P.DrawLine(R,M,R,T,W,True);  end;

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

Код второго примера достигает того же результата, как и приведенный выше, но рисует линии в логическом порядке: слева с низу — наверх, далее направо, далее вниз и затем назад налево. Рисуя этот прямоугольник от руки на бумаге, вы как будто рисуете 4 сегмента одним плавным движением (одной непрерывной линией).

Второй пример кода, рисующий прямоугольник на странице:
procedure TMainForm.GenerateLinesNoStroke(P : TPDFPage); var  W,M,T,R : TPDFFloat; begin  W:=1.0; // Ширина страницы  M:=10; // Поле  T:=PDFToMM(P.Paper.H)-M; // Верх  R:=PDFToMM(P.Paper.W)-M; // Право  // Поля страницы  P.MoveTo(M,M);  P.DrawLine(M,M,M,T,W,False);  P.DrawLine(M,T,R,T,W,False);  P.DrawLine(R,T,R,M,W,False);  P.DrawLine(R,M,M,M,W,False);  P.StrokePath; end;

Первая координата DrawLine игнорируется, когда параметр Stroke равен False, рисование начинается с текущей позиции. Из-за этого необходима команда MoveTo, которая задает начальную позицию пера. Последняя команда StrokePath сообщает движку, что рисование завершено, и что линию можно уже нарисовать.

Для одновременного создания нескольких линий можно использовать команду DrawPolyLine:

procedure DrawPolyLine(const APoints: array of TPDFCoord;const ALineWidth: TPDFFloat);

Процедура принимает массив точек и рисует линии между N и N+1-й точками. Используя эту команду, прямоугольник страницы теперь можно нарисовать с помощью следующего кода:

Пример кода для отрисовки прямоугольника:
procedure TMainForm.GeneratePolyLines(P : TPDFPage); var  W,M,T,R : TPDFFloat;  A : Array[1..5] of TPDFCoord; begin  W:=1.0; // Ширина страницы  M:=10; // Поле  T:=PDFToMM(P.Paper.H)-M; // Верх  R:=PDFToMM(P.Paper.W)-M; // Право  // Поля страницы  A[1].X:=M; A[1].Y:=M;  A[2].X:=M; A[2].Y:=T;  A[3].X:=R; A[3].Y:=T;  A[4].X:=R; A[4].Y:=M;  A[5].X:=M; A[5].Y:=M;  P.DrawPolyLine(A,1);  P.StrokePath; end;

Помните о необходимости вызова команды StrokePath, поскольку команда DrawPolyline сама не будет рисовать путь. Хотя в нарисованном прямоугольнике углов всего четыре, но массиву нужно указать 5 координат: последняя позиция в массиве — это координаты первой точки. Для замкнутых фигур, можно использовать функцию DrawPolygon: она автоматически замкнет линию, нарисовав сегмент от последней до первой точки.

 procedure DrawPolygon(const APoints: array of TPDFCoord; const ALineWidth: TPDFFloat);

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

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

Цвет заливки должен быть установлен с помощью SetColor, с параметром AStroke, установленным в значении False.

Пример кода, который нарисует прямоугольник страницы и заполнит его желтым цветом:
procedure TMainForm.GeneratePolygonsFill(P: TPDFPage); var  W,M,T,R : TPDFFloat;  L : Integer;  A : Array[1..4] of TPDFCoord; begin  W:=1.0; // Ширина страницы  M:=10; // Поле  T:=PDFToMM(P.Paper.H)-M; // Верх  R:=PDFToMM(P.Paper.W)-M; // Право  // Поля страницы  A[1].X:=M; A[1].Y:=M;  A[2].X:=M; A[2].Y:=T;  A[3].X:=R; A[3].Y:=T;  A[4].X:=R; A[4].Y:=M;  P.DrawPolygon(A,1);  P.SetColor(clYellow,False);  P.FillStrokePath; end;

7. Рисование: фигуры — прямоугольники, круги и эллипсы.

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

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

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

Ниже приведены определения различных команд рисования:

procedure DrawRect(const X, Y, W, H, ALineWidth: TPDFFloat; const AFill,                    AStroke : Boolean; const ADegrees: single = 0.0); procedure DrawRoundedRect(const X, Y, W, H, ARadius, ALineWidth: TPDFFloat;                    const AFill, AStroke : Boolean; const ADegrees: single = 0.0); procedure DrawEllipse(const APosX, APosY, AWidth, AHeight, ALineWidth: TPDFFloat;     const AFill: Boolean = True; AStroke: Boolean = True;                    const ADegrees: single = 0.0);

Приведенный ниже код нарисует 2 эллипса с одинаковым (нижним, левым) углом, но второй будет повернут на 45 градусов. Для каждого эллипса будут нарисованы ограничивающий прямоугольник вокруг него.

Пример кода, рисующего эллипсы:
 procedure TMainForm.GenerateEllipses(P: TPDFPage);  var   W,M,T,R : TPDFFloat;   L : Integer;   CX,CY : TPDFFloat;  begin   W:=1.0; // Ширина страницы   M:=10; // Поле   T:=PDFToMM(P.Paper.H)-M; // Верх   R:=PDFToMM(P.Paper.W)-M; // Право   CX:=R/4;   CY:=T/2;   P.DrawEllipse(CX-10,CY-10,40,20,W,False,True);   // Вращение вокруг нижнего левого угла   P.SetColor(clRed);   P.DrawEllipse(CX-10,CY-10,40,20,W,False,True,45);   P.SetColor(clDkGray);   P.SetPenStyle(ppsDash);   P.DrawRect(CX-10,CY-10,40,20,W,False,True);   P.DrawRect(CX-10,CY-10,40,20,W,False,True,45);  end;

Ниже на рисунке 2 можно увидеть результат этого кода и еще несколько примеров.

Habr-4_2.jpg

Рис.2. Примеры рисования примитивов.

В спецификации PDF на самом деле нет команд для рисования круга или эллипса. Вместо этого рисуются кривые Безье. Круги и эллипсы на самом деле рисуются с аппроксимацией с использованием кривых Безье. В спецификации PDF есть 3 команды рисования кривых Безье (C, V и Y), они отличаются только тем, как указываются контрольные точки для кривой.

Форма C явно указывает 2 контрольные точки.

procedure CubicCurveTo(ACtrl1, ACtrl2, ATo: TPDFCoord; const ALineWidth: TPDFFloat;     AStroke: Boolean = True); overload;

Для формы V первая контрольная точка совпадает с началом кривой, для формы Y вторая контрольная точка совпадает с конечной точкой кривой.

procedure CubicCurveToV(ACtrl2, ATo: TPDFCoord; const ALineWidth: TPDFFloat;  AStroke: Boolean = True); overload; procedure CubicCurveToY(ACtrl1, ATo: TPDFCoord; const ALineWidth: TPDFFloat;  AStroke: Boolean = True); overload;

Во всех случаях первая точка кривой Безье является текущим положением.

8. Изображения

Возможности PDF-движка не будут полностью понятны без описания работы с изображениями. Рисование изображения в PDF-документе — это двухэтапный процесс:

  1. Добавьте изображение в документ. Это приведет к получению числового идентификатора изображения.

  2. Нарисуйте изображение на странице, используя идентификатор, полученный на gthdjv шаге.

PDF-документ поддерживает список изображений в свойстве Images. Эта коллекция хранит список изображений. А само изображение можно добавить одним из следующих способов:

function AddJPEGStream(Const AStream : TStream; Width,Height : Integer): Integer; function AddFromStream(Const AStream : TStream; Handler : TFPCustomImageReaderClass;     KeepImage : Boolean = False): Integer; function AddFromFile(Const AFileName : String;                         KeepImage : Boolean = False): Integer

Имена методов говорят сами за себя. Вы можете добавить в документ любой формат изображения, который поддерживает Free Pascal; компонент выполнит все необходимые преобразования. Поскольку PDF изначально поддерживает сжатые потоки JPEG, вы можете добавить необработанные файлы JPEG в документ без изменений. После добавления изображения в список, вы сможете использовать его одним из двух методов:

procedure DrawImageRawSize(const X, Y: TPDFFloat;  const APixelWidth, APixelHeight,  ANumber: integer; const ADegrees: single = 0.0); procedure DrawImage(const X, Y: TPDFFloat;  const AWidth, AHeight: TPDFFloat;  const ANumber: integer;  const ADegrees: single = 0.0);

Первый метод (DrawImageRawSize) рисует изображения, используя их размеры в пикселях, а метод DrawImage — в текущих единицах страницы. Как и все остальные элементы, изображение можно вращать. Приведенный ниже код рисует одно и то же изображение четыре раза на странице, по одному в каждом углу, поворачивая его для каждого угла. Изображения в нижнем правом и верхнем левом углах изменяются в размере, причем нижнее правое изображение сохраняет соотношение сторон.

Пример кода вставки изображений:
procedure TMainForm.GenerateImages(P: TPDFPage); var  M,T,R : TPDFFloat;  I : Integer;  W,H : TPDFFloat;  FN : String; begin  M:=10; // Поля  T:=PDFToMM(P.Paper.H)-M; // Верхняя граница  R:=PDFToMM(P.Paper.W)-M; // Правая граница  DrawPageMargin(P);  FN:=ExtractFilePath(ParamStr(0))+'poppy.jpg';  I:=D.Images.AddFromFile(FN,False);  W:=PDFtoMM(D.Images[i].Width);  H:=PDFtoMM(D.Images[i].Height);  // Нижний левый угол  P.DrawImage(2*M,2*M,W,H,I);  // Верхний правый угол, поворот на 180 градусов  P.DrawImage(R-M,T-M,W,H,I,180);  // Нижний правый угол, сохранение пропорций.  P.DrawImage(R-M,2*M,4*M,4*M * H/W,I,90);  // Верхний левый угол, поворот на 270 градусов. Искажение.  P.DrawImage(M,T-M,4*M,4*M,I,270); end;

Ниже на рисунке 3 можно увидеть результат работы этого кода.

Habr-4_3.jpg

Рис.3. Отрисовка изображений.

9. Поддержка текста

Вероятно одна из самых сложных задач при работе с PDF — это написание текста на странице. Текст должен быть размещён с использованием шрифта. В стандарте PDF определены несколько встроенных шрифтов, которые будут эмулироваться, если они недоступны на устройстве, где просматривается PDF-документ.

Кроме того, PDF также поддерживает использование шрифтов TrueType (TTF). Если шрифт доступен на устройстве, где просматривается PDF-документ, его не нужно включать в файл. В противном случае, если шрифт недоступен, PDF не будет отображаться корректно. В таких случаях шрифт может быть встроен в сам PDF-документ.

Как и в случае с изображениями, написание текста на странице PDF-документа также состоит из двух этапов:

  1. Добавьте определение шрифта в документ. Это создаст уникальный числовой идентификатор шрифта.

  2. Напишите текст на странице, используя идентификатор шрифта, полученный на первом этапе.

Добавление шрифта можно выполнить с помощью метода AddFont:

function AddFont(AName : String) : Integer; function AddFont(AFontFile: String; AName : String) : Integer;

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

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

procedure SetFont(AFontIndex : Integer; AFontSize : Integer);

А затем текст можно нарисовать с помощью WriteText:

procedure WriteText(X,Y: TPDFFloat; AText: UTF8String; const ADegrees: single = 0.0);

Текст будет размещён по координатам X,Y, при этом базовая линия текста совпадает с координатой Y. Это означает, что текст может простираться как выше, так и ниже координаты Y. Текст может быть в формате Unicode с кодировкой UTF-8. Как и все другие команды рисования, текст можно вращать. Он рисуется с использованием цвета заливки.

Пример, который рисует текст в четырёх углах страницы:
procedure TMainForm.GenerateText(P: TPDFPage); var  M,T,R : TPDFFloat;  I : Integer; begin  M:=10; // Поля  T:=PDFToMM(P.Paper.H)-M; // Верх  R:=PDFToMM(P.Paper.W)-M; // Право  I:=D.AddFont('Helvetica');  // Установить шрифт, Helvetica, 12 пт  P.SetFont(I,12);  // Нижний левый угол  P.WriteText(2*M,2*M,'(Bottom,left)');  // Верхний левый угол, повернутый на 270 градусов  P.WriteText(2*M,T-M,'(Top,Left)',270);  // Верхний правый угол, повернутый на 180 градусов  P.WriteText(R-M,T-M,'(Top,Right)',180);  // Нижний правый угол, повернутый на 90 градусов  P.WriteText(R-M,2*M,'(Bottom,Right)',90); end;

10. Больше текста и HTML

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

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

Кроме того, PDF поддерживает встраивание гиперссылок. Это реализуется путём обозначения прямоугольника на странице как активной области. Это можно сделать с помощью метода AddExternalLink класса TPDFPage:

procedure AddExternalLink(const APosX, APosY, AWidth, AHeight: TPDFFloat;    const AURI: string;    ABorder: boolean = false);

Чтобы пометить фрагмент текста как гиперссылку, сначала нужно узнать объем текста. Для того, чтобы иметь возможность вычислять размеры текста и встраивать шрифты, в модуле fpttf реализован менеджер шрифтов. Менеджер шрифтов будет читать файл шрифта и искать метрики шрифта, т. е. размеры различных символов в шрифте. Используя эти метрики, менеджер шрифтов может затем вычислить высоту и ширину текста. Эти метрики также необходимы при встраивании определения шрифта в PDF.

Менеджер шрифтов должен быть инициализирован. Самый простой способ — использовать метод BuildFontCache, который создаст кэш шрифтов, найденных в одном или нескольких каталогах. Для примера программы мы предположим, что все шрифты находятся в каталоге с именем fonts:

 procedure TMainForm.FormCreate(Sender: TObject);  var   FontDir : String;  begin   FontDir:=ExtractFilePath(ParamStr(0))+’fonts’;   D.FontDirectory:=FontDir;   gTTFontCache.SearchPath.Add(FontDir);   gTTFontCache.BuildFontCache;  end;

После инициализации менеджера шрифтов, для вычисления ширины и высоты заданного текста (в пикселях) можно использовать методы TextWidth и TextHeight:

 function TextWidth(const AStr: utf8string; const APointSize: single): single;  function TextHeight(const AText: utf8string; const APointSize: single;   out ADescender: single): single;

Высота текста — это расстояние от базовой линии до верхней границы текста, а выносной элемент — это количество пикселей, на которое текст выступает ниже базовой линии. Количество пикселей определяется настройкой DPI в кэше шрифта (по умолчанию 72).

Собрав все это вместе, мы теперь можем разместить текст на странице, нарисовать вокруг него прямоугольник и пометить его как гиперссылку.

Пример кода для добавления текста и гиперссылки на страницу:
procedure TMainForm.GenerateHTML(P : TPDFPage); const  aText1 = 'Hello, Free Pascal!';  aURL = 'http://www.freepascal.org/';  FontName = 'FreeSans';  FontSize = 12; var  DH,H,W,M,T,AY,AX : TPDFFloat;  I : integer;  lFC: TFPFontCacheItem; begin  M:=10; // Поля  T:=PDFToMM(P.Paper.H)-M; // Верх  // Добавить шрифт в документ.  I:=D.AddFont('FreeSans.ttf', FontName);  P.SetFont(I, FontSize);  P.SetColor(clBlack, false);  AX:=2*M;  AY:=T-T/4-M;  P.WriteText(AX,AY, aText1);  // Найти метрики шрифта:  lFC := gTTFontCache.Find(FontName, False, False);  // Рассчитать высоту в пикселях согласно DPI в кэше шрифта  H:=lFC.TextHeight(aText1, FontSize, DH);  W := lFC.TextWidth(aText1, FontSize);  // Преобразовать в мм  H:=(H * cInchToMM)/gTTFontCache.DPI ;  DH:=(DH * cInchToMM)/gTTFontCache.DPI ;  W:=(W * cInchToMM)/gTTFontCache.DPI;  // Нарисовать рамку вокруг текста  P.SetColor(cldkGray, true);  P.DrawRect(AX,AY-DH,W,H+DH,1,false,true);  // Теперь создать гиперссылку на этом месте:  P.AddExternalLink(AX,AY-DH,W,H+DH,aURL,False); end;

11. Заключение

Любой, кто умеет рисовать что-либо в LCL или VCL, может создать базовый PDF. Создание PDF очень похоже на рисование на холсте. Методы немного отличаются, но идеи остаются практически одинаковыми. В настоящее время PDF API Free Pascal не поддерживает расширения PDF, такие как формы, видео и звук, но предоставляет все необходимые методы для создания базовых PDF документов.


ссылка на оригинал статьи https://habr.com/ru/articles/872142/


Комментарии

Добавить комментарий

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