Skip to content

Draw vector graphics — shapes, colours, and line styles

Use this recipe to draw filled and outlined primitives: rectangles, rounded rectangles, circles, ellipses, and lines. For each shape, you set the fill colour, stroke colour, and line width. The recipe follows examples/06-colors-and-drawing.php.

Each primitive maps to an ISO 32000-2 path object. A path object is a shape built from line and curve segments. It ends with a painting operator that tells the viewer to stroke, fill, or do both.

Terminal window
composer require nextpdf/core:^3

You do not need an optional extension. The drawing and colour application programming interface (API) has been stable since 1.0.0, and it runs on the 8.1–8.4 backport matrix.

Set the state, then draw. setFillColor(), setDrawColor(), and setLineWidth() update the graphics state. The next rect(), circle(), ellipse(), roundedRect(), or line() uses that state. Shape methods take a style argument: 'F' fills, 'S' strokes (the default), and 'DF'/'FD' does both. Internally, a filled rectangle is the path-construction re operator followed by the fill painting operator. ISO 32000-2 §8.5.3 specifies S for stroking and f for filling as the principal path-painting operators.

Colours are device colours. setFillColor(r, g, b) selects DeviceRGB. ISO 32000-2 §8.6.4.3 defines rg as the DeviceRGB non-stroking colour operator and g as the DeviceGray equivalent. A single-argument setFillColor($v) sets a grey level. The line width defaults to 1.0, and the dash pattern defaults to a solid line (§8.4.3.6).

The API surface is generated from PHPDoc. This recipe uses these methods:

  • rect(float $x, float $y, float $w, float $h, string $style = 'S'): static
  • roundedRect(float $x, float $y, float $w, float $h, float $r, string $style = 'S'): static
  • circle(float $x, float $y, float $r, string $style = 'S'): static
  • ellipse(float $x, float $y, float $rx, float $ry, string $style = 'S'): static
  • line(float $x1, float $y1, float $x2, float $y2): static
  • setFillColor(int $r, int $g = -1, int $b = -1): static / setDrawColor(...)
  • setLineWidth(float $width): static
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->addPage();
$doc->setFillColor(30, 58, 138);
$doc->rect(20, 30, 60, 40, 'F'); // filled rectangle
$doc->setDrawColor(217, 119, 6);
$doc->setLineWidth(1.0);
$doc->circle(140, 50, 20, 'S'); // stroked circle
$doc->setLineWidth(0.3);
$doc->line(20, 90, 190, 90); // thin rule
$doc->save(getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/vector.pdf');

This complete, harness-ready example honours NEXTPDF_COOKBOOK_OUTPUT and adds no entropy of its own.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Colors and Drawing');
$doc->addPage();
$doc->setFont('helvetica', 'B', 18);
$doc->cell(0, 12, 'Colors and Drawing', newLine: true);
$doc->ln(5);
// --- Filled rectangles: set fill colour, then draw with style 'F' ---
$palette = [
[30, 58, 138], [217, 119, 6], [30, 27, 75],
[239, 66, 35], [21, 128, 61],
];
$x = 15.0;
$rowY = $doc->getY();
foreach ($palette as [$r, $g, $b]) {
$doc->setFillColor($r, $g, $b);
$doc->rect($x, $rowY, 30, 20, 'F');
$x += 35.0;
}
$doc->ln(28);
// --- Outlined shapes: set draw colour + line width, draw with 'S' ---
$doc->setDrawColor(30, 58, 138);
$doc->setLineWidth(0.5);
$y = $doc->getY();
$doc->rect(15, $y, 30, 25, 'S');
$doc->roundedRect(55, $y, 30, 25, 5, 'S');
$doc->circle(110, $y + 12.5, 12.5, 'S');
$doc->ellipse(150, $y + 12.5, 18, 10, 'S');
$doc->ln(33);
// --- Lines at three widths ---
$y = $doc->getY();
$doc->setDrawColor(0);
$doc->setLineWidth(0.2);
$doc->line(15, $y, 195, $y);
$doc->setLineWidth(0.8);
$doc->line(15, $y + 5, 195, $y + 5);
$doc->setLineWidth(1.5);
$doc->line(15, $y + 12, 195, $y + 12);
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/vector.pdf';
$doc->save($out);
echo "Created vector.pdf\n";
  • The style defaults to stroke. If you omit the style argument, the shape is stroked ('S'). A shape that looks invisible is usually drawn with no draw colour set, or filled with the page-background colour. Pass 'F' to fill it.
  • Colour state persists. setFillColor() stays in effect until you change it. Reset it, for example with setFillColor(255), before the next unrelated block, or the colour carries forward.
  • Grey versus the RGB overload. setFillColor(128) is a grey level. setFillColor(128, 0, 0) is RGB. The one-argument form is not “red 128”.
  • The Y axis runs top-down in the API. The drawing helpers use the document’s top-left origin. The engine maps that to the PDF bottom-left user space for you.

Each primitive produces a handful of content-stream operators. Thousands of shapes per page stay well within the 2000 ms / 64 MB budget. Cost grows linearly with the primitive count. There is no rasterisation, so the output stays vector.

This recipe draws only the geometry your code specifies. It parses no input and makes no network access. Range-check any coordinates that come from untrusted data. This check stops a hostile value from pushing marks far outside the page.

StatementSpecClausereference_id
A path object is lines, rectangles, and Bézier curves ending in a painting operator.ISO 32000-2§8.5
S strokes and f fills a path.ISO 32000-2§8.5.3
rg sets the DeviceRGB non-stroking colour; g sets DeviceGray.ISO 32000-2§8.6.4.3
The dash-pattern initial value is [] 0, a solid line.ISO 32000-2§8.4.3.6

Reproducibility profile — structural. Vector drawing has no entropy of its own. Even so, every saved document carries a trailer /ID and date atoms. ISO 32000-2 §8.3.4 also states that the exact arrangement of graphics-state operators has no semantic significance, so a normaliser may re-order equivalent state. The supported claim is structural equality after qpdf normalisation. This recipe describes how NextPDF produces the structure. It does not assert ISO 32000-2 conformance as a blanket claim.

Not applicable. Vector drawing is a Core capability with no Premium gate.