Adding Text
The addText() method takes a plain string or a Text object. The string is the fast path; Text is
what you construct when a line needs something the string can't carry — a size other than 12, or the color,
stroke, rotation and escaping behavior covered below.
Adding Text to a Page#
Place a string on a page with addText($text, $fontStyle, $x, $y):
use Pop\Pdf\Document;
use Pop\Pdf\Document\Page;
use Pop\Pdf\Document\Font;
use Pop\Pdf\Pdf;
$document = new Document(new Page(Page::LETTER));
$document->addFont(Font::ARIAL);
$page = $document->getPage(1);
$page->addText('Invoice #1042', 'Arial', x: 100, y: 700);
Pdf::writeToFile($document, filename: __DIR__ . '/invoice.pdf');
$x, $y is the baseline start point, in the coordinate system Drawing & Paths
shares. $fontStyle can either be a name registered with Document::addFont() or a predefined style
registered with Document::addStyle() — see Styles. A bare string always renders at
size 12, the Text object default, regardless of what the page or document last used.
Construct a Text object directly for a different size, or any of the color, stroke, rotation or
escaping behavior covered below:
use Pop\Pdf\Document;
use Pop\Pdf\Document\Page;
use Pop\Pdf\Document\Page\Text;
use Pop\Pdf\Document\Font;
use Pop\Pdf\Pdf;
$document = new Document(new Page(Page::LETTER));
$document->addFont(Font::ARIAL);
$page = $document->getPage(1);
$text = new Text('Invoice #1042', size: 14);
$page->addText($text, Font::ARIAL, x: 100, y: 700);
Pdf::writeToFile($document, filename: __DIR__ . '/invoice.pdf');
The same Text object can be handed to addText() more than once — on different pages, or twice on
one page — and each placement renders independently at the $x, $y it was given:
use Pop\Pdf\Document;
use Pop\Pdf\Document\Page;
use Pop\Pdf\Document\Page\Text;
use Pop\Pdf\Document\Font;
use Pop\Pdf\Pdf;
$document = new Document();
$document->addFont(Font::ARIAL);
$label = new Text('CONFIDENTIAL', size: 10);
$page1 = new Page(Page::LETTER);
$page1->addText($label, 'Arial', x: 20, y: 20);
$page2 = new Page(Page::LETTER);
$page2->addText($label, 'Arial', x: 20, y: 20);
$document->addPage($page1);
$document->addPage($page2);
Pdf::writeToFile($document, filename: __DIR__ . '/labeled.pdf');
Size and Font#
setSize() and getSize() read and write the point size:
use Pop\Pdf\Document\Page\Text;
$text = new Text('Section Heading');
echo $text->getSize(); // 12, the default with no size argument at all
$text->setSize(24);
The font itself is not a property of Text — it resolves from the $fontStyle name at compile time,
not when addText() is called, so an unregistered $fontStyle name raises no error until then:
use Pop\Pdf\Document;
use Pop\Pdf\Document\Page;
use Pop\Pdf\Document\Page\Text;
$document = new Document(new Page(Page::LETTER));
$page = $document->getPage(1);
$page->addText(new Text('Hi', size: 12), 'Arial', x: 100, y: 700);
try {
$bytes = (string)$document; // never registered Font::ARIAL on $document
} catch (\Pop\Pdf\Build\Exception $exception) {
echo $exception->getMessage();
// Error: The font 'Arial' has not been added to the document.
}
Fonts covers registering fonts on the document and every standard font name available.
Color#
setFillColor() and setStrokeColor() each take a Pop\Color implementation — Rgb, Cmyk or
Grayscale — the same interface Drawing & Paths accepts:
use Pop\Pdf\Document\Page\Text;
use Pop\Color\Color\Rgb;
$text = new Text('Total Due', size: 18);
$text->setFillColor(new Rgb(200, 0, 0));
Fill is the color ordinary text paints in. Leaving setFillColor() uncalled writes no color operator
into the page's content stream, so the text inherits whatever fill color a shape drawn earlier on the
same page last set — give every string its own fill color rather than relying on prior state. Stroke
color is a separate value, unused on its own; Outlined Text is where it draws.
Cmyk and Grayscale work the same way as Rgb, and a page can mix all three:
use Pop\Pdf\Document\Page\Text;
use Pop\Color\Color\Cmyk;
use Pop\Color\Color\Grayscale;
$heading = new Text('Confidential', size: 18);
$heading->setFillColor(new Cmyk(0, 100, 100, 0));
$watermark = new Text('DRAFT', size: 60);
$watermark->setFillColor(new Grayscale(80));
getFillColor() and getStrokeColor() hand back that same object — no conversion between color
spaces occurs.
Outlined Text#
setStroke() records a width, a dash length and a dash gap for getStroke() to read back:
use Pop\Pdf\Document\Page\Text;
use Pop\Color\Color\Rgb;
$text = new Text('OUTLINE', size: 60);
$text->setStrokeColor(new Rgb(0, 0, 0));
$text->setStroke(3, dashLength: 8, dashGap: 4);
$text->getStroke(); // ['width' => 3, 'dashLength' => 8, 'dashGap' => 4]
A Text that never calls setStroke() still answers getStroke() with a zero width and no dash:
use Pop\Pdf\Document\Page\Text;
$text = new Text('Outline', size: 40);
$text->getStroke(); // ['width' => 0, 'dashLength' => null, 'dashGap' => null]
Those three values are bookkeeping only — setStroke()'s width and dash have no visible effect on
text, which always strokes at the device's own default line width, undashed. What puts an outline on
the page is setStrokeColor() plus the render mode, the last argument of setTextParams():
use Pop\Pdf\Document\Page\Text;
use Pop\Color\Color\Rgb;
$text = new Text('OUTLINE', size: 60);
$text->setFillColor(new Rgb(255, 255, 255));
$text->setStrokeColor(new Rgb(0, 0, 0));
$text->setTextParams(c: 0, w: 0, h: 100, v: 100, rot: 0, rend: 2);
The render mode, setTextParams()'s last argument, controls what paints:
0, the default — fill only1— stroke only; drop the whitesetFillColor()call above to get this2, used above — fill and stroke together3–7— add the glyphs to the page's clipping path instead of, or alongside, painting them, the same technique Drawing & Paths covers with shapes
Outside 0 through 7, rend raises OutOfRangeException. The other five arguments — spacing,
scale and rotation — are covered in Text Parameters.
Rotation#
setRotation() takes degrees, positive turning the string counterclockwise around its start point
and negative turning it clockwise:
use Pop\Pdf\Document\Page\Text;
$text = new Text('Draft', size: 20);
$text->setRotation(30);
echo $text->getRotation(); // 30
The pivot is the $x, $y baseline start point, so the text swings around its own left edge rather
than the page or its own center. setRotation() accepts -90 to 90; outside that range it raises
OutOfRangeException:
use Pop\Pdf\Document\Page\Text;
$text = new Text('Draft', size: 20);
try {
$text->setRotation(120);
} catch (\OutOfRangeException $exception) {
echo $exception->getMessage();
// Error: The rotation parameter must be between -90 and 90 degrees.
}
Ninety degrees either way stands a string upright or lays it on its side; setRotation() alone
cannot turn text upside down or past a quarter turn.
Text Parameters#
setTextParams() is where character spacing, word spacing and horizontal or vertical scale live —
tracking for a letter-spaced heading, or a condensed or extended weight, all in one call:
use Pop\Pdf\Document\Page\Text;
$text = new Text('TRACKED', size: 20);
$text->setTextParams(c: 4, w: 8, h: 90, v: 100);
c— character spacing in points, added between each glyphw— word spacing in points, added at each space characterh— horizontal scale, as a percentage of normal glyph widthv— vertical scale, as a percentage of normal glyph height
Defaults are c: 0, w: 0, h: 100, v: 100, rot: 0, rend: 0; calling setTextParams() with no
arguments changes nothing on a fresh Text. The same call also carries rotation and the render
mode — Rotation and Outlined Text cover those two.
rot writes to the same value setRotation() does, so calling setTextParams() after
setRotation() silently resets the rotation to whatever rot was passed — 0 unless given
explicitly:
use Pop\Pdf\Document\Page\Text;
$text = new Text('Draft', size: 20);
$text->setRotation(15);
$text->setTextParams(c: 4);
echo $text->getRotation(); // 0 — the rotation is gone
Call setTextParams() before setRotation(), or repeat the same rot value in both calls.
Multiple Strings#
setStrings() takes an array of plain strings and Text objects together, for the cases —
Wrapping to a Box is the one on this site — where several
separately-styled runs need to be measured and wrapped as one block of text:
use Pop\Pdf\Document\Page\Text;
$text = new Text();
$text->setStrings(['Regular run, ', new Text('bold run', size: 12)]);
count($text->getStrings()); // 2
Only Text\Wrap reads setStrings() — see Wrapping to a Box.
Text\Alignment always measures getString() instead, which a Text built purely from
setStrings() never sets, so pairing it with setAlignment() compiles but renders no text at all;
give an aligned Text its content with the constructor or setString().
setStrings() only escapes an element that contains a multibyte character — a plain-ASCII string in
the array passes through getStrings() exactly as written, parentheses and all, while a string with
an accented letter is both transcoded and escaped the way Escaping describes:
use Pop\Pdf\Document\Page\Text;
$text = new Text();
$text->setStrings(['Plain (parens) text', 'café (parens)']);
$text->getStrings();
// ['Plain (parens) text', 'café \(parens\)'] — only the second element is escaped
That gap closes once Wrapping to a Box lays the array out on
the page — each run is escaped again as part of that step, so a plain-ASCII element still reaches the
page correctly escaped. Don't escape it yourself first; a parenthesis already escaped before
setStrings() sees it is escaped a second time when the run is laid out.
addStringWithOffset() is a different pairing — one extra string tucked onto the same line as the
main one, nudged left or right by a kerning offset instead of starting a new line:
use Pop\Pdf\Document\Page\Text;
$text = new Text('AB', size: 40);
$text->addStringWithOffset('CD', offset: 200);
$text->getStringsWithOffset(); // [['string' => 'CD', 'offset' => 200]]
- a positive offset opens a gap between the two strings
- a negative offset pulls the second string back over the first until the glyphs overlap
addStringWithOffset()can be called more than once, adding another string-and-offset pair each timegetStringsWithOffset()always returns the full list in the order they were added
Escaping#
Every string handed to setString() — the constructor and addText()'s bare-string shortcut both
call it — is escaped by default before it is stored:
use Pop\Pdf\Document\Page\Text;
$text = new Text("Say \"hi\" (please)");
echo $text->getString(); // Say "hi" \(please\)
echo $text->getRawString(); // Say "hi" (please)
getRawString() reads the string back exactly as given, before escaping. Alignment and Wrap
escape each line once as they lay a paragraph out — a Text bound for either takes the constructor's
default true the same as one placed directly with addText().
Text::escape() is the static method doing the escaping, and it is safe to call directly on a string
that is not going into a Text object at all:
use Pop\Pdf\Document\Page\Text;
echo Text::escape('Hello (World)'); // Hello \(World\)
It covers only a backslash (\\), an opening or closing parenthesis (\(, \)), a backspace
(\b), and four whitespace characters — newline (\n), carriage return (\r), tab (\t) and form
feed (\f) — nothing else, so quotes, angle brackets and ampersands pass through unchanged.
What a font then does with an accented letter or a glyph it has no coverage for is that font's
concern, not the escaper's — see Fonts. Both the constructor and setString() take a
second, boolean argument that skips escaping entirely; pass false for a string already escaped by
hand.
See Also#
- Text Layout & Wrapping — alignment and wrapping several lines of one string into a column
- Text Streams — flowing runs of styled text across a box, with orphans carried onto a new page
- Fonts — registering the fonts
addText()'s$fontStyleargument refers to - Styles — the other thing
$fontStyleaccepts: a name carrying a font, size and color at once - Drawing & Paths — the coordinate system and color interface text shares with every shape