Для разработки различных заглушек, используемых для тестирования сервиса, пока не готова ответная часть, я иногда использую отрисовку нужной информации на 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 означает более высокую позицию на странице.
Это отличается от системы координат, используемой в 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 есть два способа указать размер страницы:
-
Использование
PaperType
иOrientation
, что установит размеры в свойствеPaper
. -
Установка
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 (или при рисовании любого замкнутого контура) внутренняя часть нарисованного контура может быть заштрихована. Это можно сделать с помощью двух команд, каждая из которых использует разный алгоритм:
-
FillStrokePath — правило ненулевого индекса (nonzero-winding number rule).
-
FillEvenOddStrokePath — правило чётный-нечётный (even-odd number rule).
Подробные описания этих правил можно прочитать, по ссылкам на страницы в Википедии. Сами правила могут давать разные результаты, в зависимости от сложности пути контура.
Цвет заливки должен быть установлен с помощью 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 можно увидеть результат этого кода и еще несколько примеров.
В спецификации 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-документе — это двухэтапный процесс:
-
Добавьте изображение в документ. Это приведет к получению числового идентификатора изображения.
-
Нарисуйте изображение на странице, используя идентификатор, полученный на 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 можно увидеть результат работы этого кода.
9. Поддержка текста
Вероятно одна из самых сложных задач при работе с PDF — это написание текста на странице. Текст должен быть размещён с использованием шрифта. В стандарте PDF определены несколько встроенных шрифтов, которые будут эмулироваться, если они недоступны на устройстве, где просматривается PDF-документ.
Кроме того, PDF также поддерживает использование шрифтов TrueType (TTF). Если шрифт доступен на устройстве, где просматривается PDF-документ, его не нужно включать в файл. В противном случае, если шрифт недоступен, PDF не будет отображаться корректно. В таких случаях шрифт может быть встроен в сам PDF-документ.
Как и в случае с изображениями, написание текста на странице PDF-документа также состоит из двух этапов:
-
Добавьте определение шрифта в документ. Это создаст уникальный числовой идентификатор шрифта.
-
Напишите текст на странице, используя идентификатор шрифта, полученный на первом этапе.
Добавление шрифта можно выполнить с помощью метода 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/
Добавить комментарий