HotPDF tidak menyertakan komponen grafik khusus, tetapi primitif drawing vektor yang disediakannya: path, persegi panjang, lingkaran, teks, warna, dan lebar garis, sepenuhnya cukup untuk membangun grafik yang bersih dan dapat diskalakan langsung di PDF. Pendekatan ini memberikan kontrol penuh atas setiap piksel tanpa ketergantungan pada library grafik eksternal
HotPDF tidak memiliki objek chart siap pakai, sehingga layout dibangun dari primitive vektor. Bar chart menjadi perulangan atas persegi panjang, line chart menjadi polyline, dan pie chart menjadi irisan berbasis sudut. Hasilnya tetap tajam saat dicetak karena bar dan sumbu adalah operator path PDF, bukan piksel layar
Record TPlotArea
Berguna untuk mendefinisikan record yang menyimpan geometri area plot sebagai satu unit, lalu meneruskannya ke prosedur drawing:
type
TPlotArea = record
X, Y: Double; // sudut kiri-bawah area plot
Width, Height: Double; // dimensi dalam poin
MinValue, MaxValue: Double; // rentang nilai data
end;
Fungsi ValueToY
Semua jenis grafik membutuhkan pemetaan dari nilai data ke koordinat halaman. Fungsi berikut melakukan normalisasi linier:
function ValueToY(const Plot: TPlotArea; Value: Double): Double;
begin
Result := Plot.Y + ((Value - Plot.MinValue) /
(Plot.MaxValue - Plot.MinValue)) * Plot.Height;
end;
Dengan MinValue = 0 dan MaxValue = 100, nilai 50 memetakan ke tengah-tengah area plot secara vertikal. Fungsi ini mengasumsikan Y bertambah ke atas (konvensi koordinat HotPDF)
DrawBarChart
procedure DrawBarChart(
PDF: THotPDF;
const Plot: TPlotArea;
const Values: array of Double;
const Labels: array of string);
var
I: Integer;
BarWidth, BarX, BarY, BarH: Double;
Page: THPDFPage;
begin
Page := PDF.CurrentPage;
BarWidth := Plot.Width / Length(Values) * 0.6;
for I := 0 to High(Values) do
begin
BarX := Plot.X + (Plot.Width / Length(Values)) * I +
(Plot.Width / Length(Values)) * 0.2;
BarY := Plot.Y;
BarH := ValueToY(Plot, Values[I]) - Plot.Y;
Page.SetRGBFillColor(0.2, 0.4, 0.8);
Page.Rectangle(BarX, BarY, BarWidth, BarH);
Page.Fill;
// label
Page.SetFont('Arial', [], 8);
Page.TextOut(BarX, Plot.Y - 12, 0, Labels[I]);
end;
end;
DrawValueAxis
procedure DrawValueAxis(PDF: THotPDF; const Plot: TPlotArea; Steps: Integer);
var
I: Integer;
StepValue, YPos: Double;
Page: THPDFPage;
begin
Page := PDF.CurrentPage;
Page.SetRGBStrokeColor(0.5, 0.5, 0.5);
Page.SetLineWidth(0.5);
for I := 0 to Steps do
begin
StepValue := Plot.MinValue +
(Plot.MaxValue - Plot.MinValue) / Steps * I;
YPos := ValueToY(Plot, StepValue);
// garis grid horizontal
Page.MoveTo(Plot.X, YPos);
Page.LineTo(Plot.X + Plot.Width, YPos);
Page.Stroke;
// label sumbu
Page.SetFont('Arial', [], 7);
Page.TextOut(Plot.X - 30, YPos - 3, 0, FormatFloat('0.#', StepValue));
end;
end;
DrawLineChart
procedure DrawLineChart(
PDF: THotPDF;
const Plot: TPlotArea;
const Values: array of Double);
var
I: Integer;
XStep, PX, PY: Double;
Page: THPDFPage;
begin
Page := PDF.CurrentPage;
Page.SetRGBStrokeColor(0.8, 0.2, 0.2);
Page.SetLineWidth(1.5);
XStep := Plot.Width / (Length(Values) - 1);
for I := 0 to High(Values) do
begin
PX := Plot.X + XStep * I;
PY := ValueToY(Plot, Values[I]);
if I = 0 then
Page.MoveTo(PX, PY)
else
Page.LineTo(PX, PY);
end;
Page.Stroke;
end;
DrawPieChart
Grafik lingkaran memerlukan pengiriman setiap irisan sebagai path kurva Bezier yang mendekati busur. Pendekatan yang lebih sederhana adalah menggunakan beberapa panggilan LineTo untuk mendekati busur dengan polygon segmen banyak:
procedure DrawPieChart(
PDF: THotPDF;
CX, CY, Radius: Double;
const Values: array of Double);
const
Colors: array[0..4] of record R, G, B: Double end = (
(R: 0.2; G: 0.4; B: 0.8),
(R: 0.8; G: 0.2; B: 0.2),
(R: 0.2; G: 0.7; B: 0.3),
(R: 0.9; G: 0.7; B: 0.1),
(R: 0.6; G: 0.2; B: 0.8)
);
var
I, J, Segments: Integer;
Total, StartAngle, EndAngle, Angle, Fraction: Double;
Page: THPDFPage;
begin
Page := PDF.CurrentPage;
Total := 0;
for I := 0 to High(Values) do
Total := Total + Values[I];
StartAngle := 0;
Segments := 36; // 36 segmen per setengah lingkaran untuk perkiraan yang mulus
for I := 0 to High(Values) do
begin
Fraction := Values[I] / Total;
EndAngle := StartAngle + Fraction * 2 * Pi;
Page.SetRGBFillColor(
Colors[I mod 5].R,
Colors[I mod 5].G,
Colors[I mod 5].B);
Page.MoveTo(CX, CY);
for J := 0 to Segments do
begin
Angle := StartAngle + (EndAngle - StartAngle) * J / Segments;
Page.LineTo(CX + Cos(Angle) * Radius, CY + Sin(Angle) * Radius);
end;
Page.ClosePath;
Page.Fill;
StartAngle := EndAngle;
end;
end;
Menyusun grafik ke halaman
var
Plot: TPlotArea;
Data: array of Double;
Labels: array of string;
begin
Plot.X := 72; Plot.Y := 300;
Plot.Width := 400; Plot.Height := 250;
Plot.MinValue := 0; Plot.MaxValue := 100;
Data := [45, 72, 38, 91, 60];
Labels := ['Jan', 'Feb', 'Mar', 'Apr', 'May'];
DrawValueAxis(PDF, Plot, 5);
DrawBarChart(PDF, Plot, Data, Labels);
end;
Setiap prosedur dapat dikombinasikan atau dilapisi. Grafik vektor yang dihasilkan diskalakan dengan sempurna ke resolusi printer apa pun karena disimpan sebagai instruksi path PDF, bukan raster bitmap