Τεχνικό Άρθρο

Σχεδίαση Γραφημάτων σε PDF με Αρχέγονα (Primitives) του HotPDF

Το HotPDF δεν έχει (has no) αντικείμενο γραφήματος (chart object). Δεν υπάρχει (There is no) TPDFChart, ούτε (no) AddBarSeries, τίποτα που (that) παίρνει έναν πίνακα αριθμών και παραδίδει πίσω (hands back) ένα αποδοσμένο (rendered) γράφημα (graph). Αυτό που (What) σας (you) δίνει αντί αυτού (instead) είναι (is) ένας καμβάς (canvas) σελίδας (page) με το (the) ίδιο λεξιλόγιο (vocabulary) χαμηλού επιπέδου που (every) χρησιμοποιεί (uses) κάθε (every) μοντέλο (model) σχεδίασης PDF: ορθογώνια (rectangles), γραμμές, κύκλους, γεμίσματα (fills), περιγράμματα (strokes), και (and) κείμενο τοποθετημένο (placed) σε ακριβείς συντεταγμένες (exact coordinates). Ένα γράφημα σε (in) ένα έγγραφο HotPDF είναι επομένως (therefore) κάτι (something) που εσείς χτίζετε (build), όχι κάτι (not something) που (you) ζητάτε (request). Αυτό ακούγεται σαν περισσότερη (more) δουλειά από (than) ό,τι είναι (it is). Αφού γράψετε τα μαθηματικά των συντεταγμένων (coordinate math) μία φορά (once), ένα (a) γράφημα (chart) ράβδων (bar) είναι (is) ένας (a) βρόχος (loop) πάνω (over) από ορθογώνια, ένα (a) γράφημα γραμμών (line chart) είναι (is) μια (a) polyline, και (and) ένα (a) γράφημα πίτας είναι ένας ανεμιστήρας (fan) από (of) τόξα (arcs), και εσείς ελέγχετε κάθε pixel του αποτελέσματος (result)

Αυτό έχει σημασία (matters) επειδή η εναλλακτική λύση στην οποία καταφεύγουν (reach for) οι άνθρωποι (people) πρώτα, η ραστεροποίηση (rasterizing) ενός (a) στοιχείου ελέγχου (control) γραφήματος (chart) της οθόνης (screen) σε (to) ένα bitmap και (and) η επικόλληση (pasting) της (the) εικόνας στη (into the) σελίδα, σας δίνει (gives you) ένα (a) γράφημα κλειδωμένο (locked) στην (to) ανάλυση οθόνης (screen resolution) που (that) εκτυπώνεται (prints) θολά (fuzzy) και (and) διογκώνει (bloats) το (the) αρχείο. Σχεδιάζοντας (Drawing) το γράφημα (the chart) με τα διανυσματικά (vector) αρχέγονα (primitives) του HotPDF διατηρείτε (keeps) την έξοδο (output) ευκρινή σε οποιοδήποτε ζουμ (zoom) και οποιοδήποτε DPI εκτύπωσης (print), επειδή (because) οι (the) ράβδοι (bars) και οι (and) άξονες (axes) είναι (are) πραγματικοί τελεστές (operators) διαδρομής PDF (real PDF path), όχι pixel. Το κόστος είναι ότι εσείς (you) κατέχετε (own) τη διάταξη (layout). Οι (The) μηχανισμοί (mechanics) καταλήγουν (come down) σε μερικές (few) κινήσεις: η μία ανατροπή (flip) συντεταγμένων (coordinate) που (that) πιάνει (catches) τους πάντες (everyone), ένα (a) δουλεμένο (worked) γράφημα (chart) ράβδων (bar), το κόλπο (trick) polyline για (for) γραφήματα (charts) γραμμών (line), και τα μαθηματικά (math) τόξου (arc) για τα (for) κομμάτια (slices) πίτας (pie)

Το (The) μόνο (only) δύσκολο (hard) κομμάτι είναι (is) το (the) σύστημα συντεταγμένων

Τα γραφικά της οθόνης τοποθετούν (put) την (the) αρχή (origin) στην επάνω αριστερή γωνία (top-left) με (with) το Y να αυξάνεται (growing) προς τα κάτω (downward). Το PDF κάνει το αντίθετο (opposite). Η αρχή (The origin) βρίσκεται (sits) στην κάτω αριστερή (bottom-left) γωνία της σελίδας και το Υ αυξάνεται προς τα πάνω, μετρούμενο σε σημεία (1/72 ίντσας). Κάθε (Every) κλήση (call) σχεδίασης στο HotPDF, TextOut, Rectangle, MoveTo, LineTo, Circle, χρησιμοποιεί (uses) αυτή (that) τη σύμβαση κάτω αριστερά (bottom-left), Υ-προς τα πάνω (Y-up convention). Εάν μεταφέρετε (carry over) τα ένστικτα (instincts) των (of) γραφικών (graphics) οθόνης (screen), το (your) πρώτο (first) σας γράφημα (chart) σχεδιάζεται (draws) ανάποδα (upside down) και (and) βγαίνει έξω (runs off) από το (the) κάτω (bottom) μέρος (of the) της σελίδας

Επομένως η πραγματική (real) δουλειά σε (in) κάθε γράφημα (chart) είναι (is) μία (one) αντιστοίχιση (mapping): μετατρέψτε μια (a) τιμή δεδομένων σε (into) συντεταγμένη Y που (that) σέβεται (respects) το Y-προς-τα-πάνω (Y-up). Αποφασίστε (Decide on) για (a) ένα ορθογώνιο πλοκής (plot rectangle), τέσσερις αριθμούς (numbers) για την (for the) περιοχή (area) που (the) ζει (lives in) το γράφημα (chart), στη συνέχεια (then) αντιστοιχίστε τη μικρότερη (smallest) τιμή στα δεδομένα σας (your data) στην (to the) κάτω (bottom) άκρη (edge) και (and) τη μεγαλύτερη (largest) στην κορυφή (top). Για (For) μια (a) ράβδο τιμής V σε (on) μια κλίμακα (scale) που πηγαίνει από (runs from) το 0 έως τη MaxValue, το (the) επάνω (top) άκρο (edge) της (the) ράβδου είναι PlotBottom + (V / MaxValue) * PlotHeight, και η ράβδος αυξάνεται προς τα πάνω (grows up) από το PlotBottom. Γράψτε (Get) σωστά αυτήν τη (that) μία (one) έκφραση (expression) και όλα τα υπόλοιπα (everything else) είναι (is) απλή καταγραφή (bookkeeping). Ο παρακάτω (below) βοηθός (helper) κρατά (holds) τη (the) γεωμετρία της πλοκής (plot) και (and) κάνει (does) τη (the) μετατροπή (conversion), έτσι (so) ο (the) κώδικας (code) σχεδίασης δεν (never) αγγίζει ποτέ (touches) μη επεξεργασμένη (raw) αριθμητική (arithmetic) δύο φορές (twice):

type
  TPlotGeom = record
    Left, Bottom, Width, Height: Single;
    MaxValue: Single;
    function ValueToY(Value: Single): Single;
  end;

function TPlotGeom.ValueToY(Value: Single): Single;
begin
  // Scale the value into the plot height, then offset from the bottom
  Result := Bottom + (Value / MaxValue) * Height;
end;

Μια κρίση (One judgement call) κρύβεται (is hiding) στο (in) MaxValue. Αν το ορίσετε στο ακριβές (exact) μεγαλύτερο σημείο (point) δεδομένων, η υψηλότερη ράβδος (tallest bar) αγγίζει (touches) το επάνω άκρο της πλοκής και φαίνεται (looks) κομμένη (clipped). Στρογγυλοποιήστε (Round it up) την σε έναν (to a) καθαρό (clean) αριθμό (number) πάνω από το (above the) μέγιστο (maximum), ας πούμε στο επόμενο (next) πολλαπλάσιο (multiple) του (of) 10 ή 100, έτσι ώστε (so) το γράφημα να έχει χώρο ελευθερίας (headroom) και οι ετικέτες (labels) των (the) γραμμών πλέγματος (gridline) να (read) διαβάζονται ως (as) στρογγυλοί αριθμοί (round figures) μάλλον παρά ό,τι έτυχε να πιάσουν (peaked at) ως κορυφή (peak) τα δεδομένα (data)

Ένα (A) γράφημα (chart) ράβδων (bar) είναι (is) ένας (a) βρόχος (loop) πάνω (over) από (of) ορθογώνια

Έχοντας τακτοποιήσει την (With the) αντιστοίχιση (mapping), ένα (a) γράφημα ράβδων γράφεται μόνο (writes itself) του. Διαιρέστε το πλάτος της πλοκής (plot width) σε μια θέση (one slot) ανά (per) κατηγορία, αφήστε (leave) ένα κενό (gap) μεταξύ (between) των ράβδων για (so) να μην (do not) ακουμπούν (touch), και σχεδιάστε (draw) κάθε ράβδο ως (as) ένα γεμισμένο (filled) ορθογώνιο του οποίου (whose) το ύψος προέρχεται από το ValueToY. Το Rectangle του HotPDF παίρνει την κάτω-αριστερή (lower-left) γωνία συν (plus) ένα (a) πλάτος (width) και ένα (a) ύψος (height), το οποίο ευθυγραμμίζεται (lines up) ακριβώς (exactly) με μια ράβδο που αυξάνεται (grows) προς τα πάνω από (from) τη γραμμή βάσης (baseline). Ορίστε το χρώμα γεμίσματος πρώτα (first), τοποθετήστε (lay down) τη (the) διαδρομή (path), και στη συνέχεια καλέστε το (call) Fill για να τη βάψετε (paint it). Η ετικέτα της (The) κατηγορίας πηγαίνει (goes) κάτω από (below) τη γραμμή βάσης (baseline), η (the) τιμή (value) πάνω από (above) τη (the) ράβδο (bar):

procedure DrawBarChart(Page: TPDFPage; const Geom: TPlotGeom; const Labels: TArray<string>; const Values: TArray<Single>);
var
  I: Integer;
  SlotWidth, Gap, BarWidth, BarX, BarHeight, TopY: Single;
begin
  if Length(Values) = 0 then Exit;
  
  SlotWidth := Geom.Width / Length(Values);
  Gap := SlotWidth * 0.2;            // 20% gap between bars
  BarWidth := SlotWidth - Gap;
  
  Page.SetFont('Helvetica', [], 9);
  Page.SetRGBFillColor(60, 120, 200); // Muted blue
  
  for I := 0 to High(Values) do
  begin
    BarX := Geom.Left + (I * SlotWidth) + (Gap / 2);
    TopY := Geom.ValueToY(Values[I]);
    BarHeight := TopY - Geom.Bottom;
    
    // Draw the bar
    Page.Rectangle(BarX, Geom.Bottom, BarWidth, BarHeight);
    Page.Fill;
    
    // Values on top, labels underneath
    Page.TextOut(BarX, TopY + 5, 0, Format('%.0f', [Values[I]]));
    Page.TextOut(BarX, Geom.Bottom - 15, 0, Labels[I]);
  end;
end;

Δύο (Two) λεπτομέρειες (details) κερδίζουν τη θέση τους. Το (The) κενό (Gap) είναι (is) ένα (a) κλάσμα (fraction) της (of the) θέσης (slot) αντί (rather than) για (for) ένας (a) σταθερός (fixed) αριθμός (count) σημείων (point), έτσι (so) οι (the) ράβδοι παραμένουν αναλογικά (proportionally) σε απόσταση (spaced) είτε σχεδιάζετε τέσσερις (four) κατηγορίες (categories) είτε σαράντα (forty). Και η (And the) ετικέτα (label) τιμής (value) τοποθετείται (is positioned) με το (with the) ίδιο ύψος που προέρχεται από το ValueToY που χρησιμοποιεί (uses) η (the) ράβδος (bar), οπότε (so) κάθεται (sits) πάντα (always) ακριβώς πάνω (just above) από τη (its) δική της (own) ράβδο αντί (instead of) να επιπλέει (floating) σε μια (at a) μαντεμένη μετατόπιση (guessed offset). Αν (If) θέλετε (you want) οριζόντιες γραμμές πλέγματος πίσω από (behind) τις (the) ράβδους, σχεδιάστε τις πριν από το (before the) βρόχο (loop): διαλέξτε (pick) τρεις (three) ή (or) τέσσερις στρογγυλές (round) τιμές, τρέξτε το ValueToY σε καθεμία, και γράψτε (stroke) μια (a) αχνή (faint) γραμμή κατά (across) πλάτος της (the) πλοκής σε (at) αυτό το Y. Σχεδιάζοντάς τες (Drawing them) πρώτα (first) τις τοποθετεί πίσω από τις (the) ράβδους (bars) στο μοντέλο στοίβαξης του ζωγράφου (painter's-model stacking) που χρησιμοποιεί (uses) το PDF

Άξονες (Axes), σημάδια ελέγχου (ticks) και ετικέτες είναι (are) απλώς (just) περισσότερες γραμμές και κείμενο

Το (The) γράφημα (chart) δεν είναι (is not) τελειωμένο (finished) μέχρι (until) ένας (a) αναγνώστης (reader) να μπορεί (can) να καταλάβει (tell) τι (what) σημαίνουν (mean) οι ράβδοι (bars), και (and) αυτό (that) είναι (is) εξ ολοκλήρου (entirely) εργασία (work) με άξονες (axis). Ο κάθετος (vertical) άξονας (axis) είναι μια περιγραμμένη (stroked) γραμμή προς τα πάνω στο (up the) αριστερό (left) άκρο (edge) της πλοκής με (with) μια χούφτα (handful) σημάδια (tick marks) και (and) τις τιμές (values) τους (their). Επαναχρησιμοποιήστε το ValueToY έτσι ώστε (so) τα σημάδια (ticks) να προσγειώνονται (land) στην (at the) ίδια κλίμακα (scale) που (the) χρησιμοποιούν οι ράβδοι, διαφορετικά (otherwise) μια (a) ράβδος και η γραμμή πλέγματός της διαφωνούν (disagree) και το (the) γράφημα ψεύδεται αθόρυβα (lies quietly):

procedure DrawYAxis(Page: TPDFPage; const Geom: TPlotGeom; TickCount: Integer);
var
  I: Integer;
  Val, TickY: Single;
begin
  Page.SetLineWidth(1.0);
  Page.SetRGBStrokeColor(0, 0, 0);
  
  // The spine
  Page.MoveTo(Geom.Left, Geom.Bottom);
  Page.LineTo(Geom.Left, Geom.Bottom + Geom.Height);
  Page.Stroke;
  
  // The ticks and labels
  for I := 0 to TickCount do
  begin
    Val := (Geom.MaxValue / TickCount) * I;
    TickY := Geom.ValueToY(Val);
    
    Page.MoveTo(Geom.Left - 3, TickY);
    Page.LineTo(Geom.Left, TickY);
    Page.Stroke;
    
    // Push the text to the left of the tick
    Page.TextOut(Geom.Left - 25, TickY - 3, 0, Format('%.0f', [Val]));
  end;
end;

Οι (The) ετικέτες (Labels) είναι εκεί (where) που τα γραφήματα (charts) χαλάνε πιο συχνά (most often break) στην παραγωγή (in production), και (and) η αποτυχία (failure) είναι (is) πάντα η (the) ίδια (same): κείμενο που χωρούσε (fit) στην (on) οθόνη ξεπερνά (overruns) τον (its) χώρο (space) του (its) στο PDF. Μεγάλα ονόματα κατηγοριών (Long category names) συγκρούονται (collide) με (with) τους (their) γείτονές τους (neighbors), και τα εντοπισμένα (localized) ονόματα μηνών όπως "septembre" ή "Dezember" είναι πιο πλατιά (wider) από το (than the) αγγλικό (English) "Sep" με το οποίο (with which) κάνατε δοκιμές (tested). Δεν (There is no) υπάρχει καμία αυτόματη προσαρμογή μεγέθους (autosize) για (to) να σας (you) σώσει (rescue) εδώ, οπότε αφήστε πραγματικό (real) περιθώριο κάτω από (under) τη γραμμή βάσης (baseline), συρρικνώστε (shrink) τη (the) γραμματοσειρά ένα (a) σημείο (point) ή (or) δύο για (for) πυκνά σύνολα κατηγοριών, και (and) αν (if) τα ονόματα (names) είναι (are) πραγματικά (genuinely) μεγάλα (long), περιστρέψτε τα (rotate them). Το TextOut παίρνει μια γωνία (angle) ως το (its) τρίτο όρισμά (argument) του (its), έτσι (so) περνώντας το (passing) 90 στέκεται (stands) η ετικέτα (label) όρθια (on end) και (and) σας αγοράζει χώρο (room) χωρίς επικάλυψη (overlap). Δοκιμάστε (Test) τη (the) διάταξη (layout) με τη (your) μεγαλύτερη (widest) αναμενόμενη (expected) ετικέτα σας (your label), όχι τη μικρότερή σας, προτού (before) το (the) έγγραφο εξόδου (export) σταλεί (ships)

Γραφήματα γραμμών (Line charts): μία polyline μέσα (through) από αντιστοιχισμένα σημεία

Ένα γράφημα (chart) γραμμών (line) επαναχρησιμοποιεί (reuses) ολόκληρη (entire) την αντιστοίχιση τιμών και (and) αλλάζει μόνο (only) πώς (how) συνδέονται τα (the) σημεία (points). Αντί (Instead of) για (of) ένα ορθογώνιο (rectangle) ανά (per) κατηγορία, περπατάτε (walk) τα δεδομένα (data) μία φορά (once), μετατρέπετε κάθε (each) τιμή (value) στο (to its) (X, Y) της (its) με ValueToY, και συρράβετε (stitch) τα σημεία (points) μαζί (together) με (with) ένα (a) μεμονωμένο (single) MoveTo ακολουθούμενο από (by) κλήσεις (calls) LineTo, με ένα περίγραμμα (stroked) στο (at the) τέλος (end). Το πρώτο (first) σημείο (point) ανοίγει (opens) τη διαδρομή (path)· κάθε (every) μεταγενέστερο (later) σημείο (point) την επεκτείνει (extends it):

procedure DrawLineChart(Page: TPDFPage; const Geom: TPlotGeom; const Values: TArray<Single>);
var
  I: Integer;
  IntervalWidth, PointX, PointY: Single;
begin
  if Length(Values) < 2 then Exit;
  
  IntervalWidth := Geom.Width / (Length(Values) - 1);
  
  Page.SetLineWidth(2.0);
  Page.SetRGBStrokeColor(200, 60, 60); // Red line
  
  for I := 0 to High(Values) do
  begin
    PointX := Geom.Left + (I * IntervalWidth);
    PointY := Geom.ValueToY(Values[I]);
    
    if I = 0 then
      Page.MoveTo(PointX, PointY)
    else
      Page.LineTo(PointX, PointY);
  end;
  
  Page.Stroke;
end;

Σημειώστε (Note) τη (the) διαφορά στην απόσταση (spacing). Ένα (A) γράφημα (chart) ράβδων διαιρεί (divides) το (the) πλάτος με (by) τον αριθμό (number) των (of) ράβδων, επειδή κάθε ράβδος (bar) κατέχει (owns) μια (a) θέση (slot). Ένα γράφημα γραμμών (line chart) διαιρεί (divides) με τον αριθμό των διαστημάτων (intervals), Count - 1, επειδή το (the) πρώτο (first) και το τελευταίο σημείο βρίσκονται (sit) στα (on the) άκρα της πλοκής και η (the) γραμμή (line) εκτείνεται (spans) στα (the) κενά μεταξύ (between) τους (them). Το μπερδεύοντάς τα αυτά τα δύο είναι ο (is the) συνήθης (usual) λόγος που ένα γράφημα (chart) γραμμών παρασύρεται (drifts) μισή (half-slot) θέση μακριά από (off from) το (the) γράφημα ράβδων που προορίζεται (meant) να επικαλύψει (overlay). Αν (If) θέλετε ένα σημάδι (marker) σε (at) κάθε (each) σημείο δεδομένων (data point), ρίξτε (drop) έναν (a) μικρό (small) Circle και Fill σε κάθε (X, Y) αφού (after) σχηματιστεί η polyline

Γραφήματα (Charts) πίτας (Pie): τόξα, ή σφήνες (wedges) αν (if) το (it) κρατήσετε (keep) απλό

Τα κομμάτια (slices) πίτας (Pie) είναι (are) το (the) ένα (one) σχήμα (shape) που (that) χρειάζεται (needs) τριγωνομετρία (trigonometry), επειδή (because) μια σφήνα οριοθετείται (is bounded) από (by) δύο (two) ακτίνες (radii) και (and) ένα (an) τόξο (arc). Η ειλικρινής έκδοση (honest version) σαρώνει (sweeps) το (the) τόξο (arc) με βήματα μικρών ευθύγραμμων τμημάτων κατά μήκος (along) της (the) περιφέρειας, το οποίο πλησιάζει (approximates) την καμπύλη τόσο (closely) κοντά ώστε (that) κανένας (no) αναγνώστης (reader) να μην μπορεί (can) να (to) το καταλάβει (tell). Η γωνία σάρωσης κάθε κομματιού είναι (is) το (its) μερίδιό (share) του από (of) το σύνολο, (Value / Total) * 2π, και συγκεντρώνετε (accumulate) την (the) τρέχουσα (running) γωνία (angle) καθώς πηγαίνετε (go) γύρω (around):

uses Math; // for Sin, Cos

procedure DrawPieWedge(Page: TPDFPage; CX, CY, Radius, StartRad, EndRad: Single);
var
  Steps, I: Integer;
  StepAngle, CurrentRad, PX, PY: Single;
begin
  Steps := Max(10, Trunc((EndRad - StartRad) * 20)); // Adjust for smoothness
  StepAngle := (EndRad - StartRad) / Steps;
  
  Page.MoveTo(CX, CY); // Center apex
  
  for I := 0 to Steps do
  begin
    CurrentRad := StartRad + (I * StepAngle);
    PX := CX + (Cos(CurrentRad) * Radius);
    PY := CY + (Sin(CurrentRad) * Radius);
    Page.LineTo(PX, PY);
  end;
  
  Page.LineTo(CX, CY); // Close back to center
  Page.Fill;
end;

Η διαδρομή από το κέντρο προς τα έξω (center-out path), πρώτα η κορυφή (apex first), μετά το (the) τόξο (arc), μετά πίσω στην κορυφή, δίνει (gives) μια κλειστή σφήνα (closed wedge) που (that) το (the) Fill βάφει συμπαγώς (solidly). Ο αριθμός των (The) τμημάτων ανταλλάσσει (trades) την (the) ομαλότητα (smoothness) με το μέγεθος διαδρομής (path size): περίπου (roughly) δύο (two) μοίρες (degrees) ανά (per) βήμα (step) φαίνονται (looks) στρογγυλές (round) σε (at) οποιαδήποτε (any) λογική (sensible) ακτίνα (radius) χωρίς (without) να παράγουν μια (an) τεράστια ροή (content) περιεχομένου. Αν (If) δεν (not) χρειάζεστε (need) έναν αληθινό (true) κύκλο, μπορείτε να (you can) παραλείψετε εντελώς την (the) τριγωνομετρία (trig) και (and) να αποδώσετε (render) τα ίδια (same) δεδομένα (data) ως μια μόνη οριζόντια στοιβαγμένη (stacked) ράβδο, όπου το πλάτος κάθε τμήματος (each segment's) είναι ανάλογο (proportional) με (to) το (its) μερίδιό (share) του. Αυτό (That) είναι (is) συχνά (often) πιο (more) ευανάγνωστο (legible) από (than a) μια πίτα (pie) ούτως ή άλλως (anyway), και (and) είναι (is) απλώς ο κώδικας ράβδου (bar code) με (with) τα ορθογώνια απλωμένα (laid) από άκρη (end) σε άκρη (to end). Προτιμήστε την έκδοση (version) του (the) τόξου (arc) μόνο όταν ο (the) σχεδιασμός (design) απαιτεί (calls for) έναν (an) πραγματικό (actual) κύκλο

Τίποτα από αυτά (None of this) δεν εξαρτάται από καμία εγκατεστημένη (installed) βιβλιοθήκη (library) γραφημάτων (chart), το οποίο (which) είναι το (is the) αθόρυβο (quiet) πλεονέκτημα (advantage) της σχεδίασης των (of drawing the) αρχέγονων (primitives) απευθείας (directly). Οι (The) ίδιες κλήσεις (calls) σχεδίασης σε (drawing) καμβά (canvas) που (that) τοποθετούν (place) ένα λογότυπο (logo) ή (or) ένα κουτί (box) υπογραφής, κατασκευάζουν αυτά τα (these) γραφήματα, και το (and the) ίδιο (same) TextOut που προσθέτει ετικέτα (labels) σε (a) ένα πεδίο φόρμας (form field), προσθέτει ετικέτα (labels) σε έναν (an) άξονα (axis). Βάλτε (Put) τη (the) γεωμετρία (geometry) πλοκής (plot) σε μια (in a) εγγραφή (record), αντιστοιχίστε τις (the) τιμές στο (to) Y μία φορά (once), και ένα (and a) γράφημα ράβδων (bar), γραμμών (line), ή (or) πίτας (pie chart) είναι μια (is a) σύντομη (short) ρουτίνα (routine) πάνω στα (over) Rectangle, LineTo, και Circle που μπορείτε να (you can) ρίξετε (drop) σε (into) οποιαδήποτε αναφορά (report). Οι (The) κλήσεις (calls) Rectangle, MoveTo, LineTo, Circle, Fill, Stroke, και TextOut που χρησιμοποιούνται εδώ αποτελούν (are) μέρος (part) του (of the) Στοιχείου Ελέγχου HotPDF για (for) Delphi και (and) C++Builder